1. Introducción
Los grĆ”ficos e infografĆas son, cada vez mĆ”s, una parte importante de la mayorĆa de textos escritos, ya sean estos un informe tĆ©cnico, un artĆculo de periódico o un TFG. En el caso de la ciencia de datos, como puede verse abajo en la infografĆa, el anĆ”lisis y la exploración de los datos es un proceso iterativo en el que la visualización y la generación de grĆ”ficos ocupa un lugar destacado.
Uno de los instrumentos y tareas fundamentales de un cientĆfico de datos es la capacidad de realizar visualizaciones de datos apropiadas y convincentes. El anĆ”lisis grĆ”fico no solo ayuda en la exploración y comprensión de los datos, sino que es fundamental a la hora de mostrar las posibles relaciones entre variables, descubrir relaciones o patrones ocultos, y descartar o sugerir nuevas preguntas sobre los datos.
El entorno R tiene diversos sistemas para visualizar datos, los dos mĆ”s utilizados son el sistema grĆ”fico de R-base y el ecosistema asociado al paquete ggplot2. Por diversas razones, en el curso usaremos el entorno ggplot2 para hacer nuestros grĆ”ficos; de hecho, en la actualidad ggplot2, dada su rapidez en la iteración entre grĆ”ficos, versatilidad y la cuidada estĆ©tica que tienen sus grĆ”ficos, se ha convertido, al menos por el momento, en el sistema estĆ”ndar para hacer grĆ”ficos en R. Por ejemplo, Āæcómo creĆ©is que hace la BBC sus grĆ”ficos?, evidentemente con R y ggplot2. Puedes ver uno de sus repositorios aquĆ y su cookbook aquĆ.
Con ggplot2 es sencillo hacer grƔficos con calidad para ser publicados o mostrados, ademƔs de que, dada su sintaxis modular, hace sencillo el reutilizar los grƔficos durante el proceso de anƔlisis. El paquete ggplot2 fue inicialmente desarrollado por Hadley Wickham, pero actualmente el ecosistema ggplot es el resultado de toda una comunidad de usuarios que contribuye a enriquecer el sistema grƔfico con sus extensiones y paquetes auxiliares.
En palabras de Hadley en su libro sobre ggplot2:
ggplot2 is an R package for producing statistical, or data, graphics, but it is unlike most other graphics packages because it has a deep underlying grammar. This grammar, based on the Grammar of Graphics (Wilkinson 2005), is made up of a set of independent components that can be composed in many different ways. This makes ggplot2 very powerful because you are not limited to a set of pre-specified graphics, but you can create new graphics that are precisely tailored for your problem. This may sound overwhelming, but because there is a simple set of core principles and very few special cases, ggplot2 is also easy to learn (although it may take a little time to forget your preconceptions from other graphics tools).
SĆ, con ggplot2 es āfĆ”cilā hacer rĆ”pidamente grĆ”ficos de calidad, PERO dominar todos los detalles del paquete sĆ que es complicado, pero no nos hace falta conocerlo todo. AdemĆ”s, hay que tener en cuenta que ggplot2 es un paquete/entorno en constante evolución. Actualmente estĆ” en la versión 3.2.1.
Para entender esta idea de la constante evolución y el papel que tiene la comunidad de usuarios en el desarrollo de R y sus paquetes puedes leer este tweet y las respuestas a Ć©l. En el tweet sólo se anuncia una pequeƱa mejora en como ggplot2 gestiona los tĆtulos de los grĆ”ficos pero genera reacciones en la comunidad de usuarios.
La redes sociales pueden hacerse eco de la evolución de un paquete, pero donde habitualmente se produce la discusión/colaboración entre usuarios es en plataformas como Github. Por ejemplo, puedes ver como se gestó estĆ” pequeƱa mejora aquĆ. Fue la issue 3252 de ggplot2. Otro ejemplo, justo cuando estaba escribiendo este pĆ”rrafo, leĆ este otro tweet anunciando otra mejora en ggplot2.
La pĆ”gina web de ggplot2 puedes encontrarla aquĆ. En ella puedes encontrar documentos de ayuda y la referencia oficial. Para darte cuenta de todo lo que se puede hacer con el ecosistema ggplot visita esta pĆ”gina donde podrĆ”s ver los 58 āpaquetes auxiliaresā o extensiones a ggplot2.
Para hacer ābuenosā grĆ”ficos con ggplot2no sólo es necesario entender la sintaxis y los pormenores del paquete, sino que quizĆ”s se necesite algo mĆ”s. Por ejemplo, algo de experiencia y cierta capacidad visual y estĆ©tica; incluso hay quien dice que hacer buenos grĆ”ficos es un arte. Para intentar mejorar vuestros grĆ”ficos o evitar ciertos errores, aquĆ tenĆ©is algunas reglas/consejos, y aquĆ un curso completo sobre visualización con bookdown incluido{target="_blank"}.
Otros dos libros sobre visualización con el código de los ejemplos hechos con ggplot2, aquĆ y aquĆ. Finalmente, algunos consejos de la BBC sobre visualización.
Como también se aprende los errores, aquà tienes un articulo de The Economist donde muestran errores que ellos mismos han cometido haciendo grÔficos. AdemÔs pueden descargarse los datos.Otro ejemplo de grÔfico un poquito tendencioso, esta vez del Washington Post:
2. Ideas bƔsicas sobre ggplot2
Ya se dijo que ggplot2 es un paquete R desarrollado por Hadley Wickham, aunque actualmente es el resultado de la colaboración de múltiples desarrolladores. ggplot2 implementa en R The Grammar of Graphics de L. Wilkinson, un sistema coherente para describir y construir grÔficos. El énfasis de ggplot2 estÔ en la exploración rÔpida de datos, especialmente de datos de alta dimensionalidad. Con ggplot2 es sencillo ir transformando el grÔfico mientras se van analizando los datos.
Para empezar a entender la āfilosofĆaā de ggplot2, os planteo una pregunta medio retórica: ĀæquĆ© vemos en el grĆ”fico de abajo?

Pues sĆ, es un grĆ”fico de puntos y nos ayuda a ver las relaciones que existen entre 3 variables. Estamos habituados a ello, pero vamos a pensar en el grĆ”fico desde la óptica de āThe Grammar of Graphicsā implementada en el paquete ggplot2.
En nuestro grÔfico se representan por medio de puntos, en el espacio X-Y, y mediante los distintos colores de los puntos, las observaciones de 3 variables. Bien, nada muy novedoso, todos los sistemas grÔficos hacen este tipo de grÔficos. Los grÔficos de ggplot2 se realizan mediante la superposición de elementos/capas. Podemos pensar que el grÔfico que hemos visto es una capa. ¿Cómo creamos este grÔfico o capa en ggplot2?
Pues, una de las principales ideas para entender ggplot2 es que cada capa de un grƔfico tiene 3 componentes o elementos principales:
los datos que se van a representar (sencillo, para hacer un grÔfico hacen falta datos). Para ello utilizaremos generalmente la función ggplot()
un conjunto de propiedades estĆ©ticas asociadas a alguna variable del conjunto de datos. Por ejemplo, la variable Sepal.Lenght estĆ” asociada al eje X, ala posición en el eje X. Por su parte, el color de los puntos (otra caracterĆstica visual o estĆ©tica) estĆ” asociado a los valores de la variable Species. Es decir, usando la terminologĆa de ggplot2, las distintas variables estĆ”n asociadas o mapeadas a determinadas caracterĆsticas estĆ©ticas. El mapeo de variables con estĆ©ticas se harĆ” con la función aes() de aesthetics. (esto ya no es tan estĆ”ndar, se explica en breve)
el elemento geomĆ©trico que se va a representar. En nuestro caso el elemento geomĆ©trico que se utiliza para representar los valores de las variables son los puntos, pero podrĆan haber sido las lineas o las barras ⦠Para especificar el elemento geomĆ©trico que vamos a usar en nuestro grĆ”fico se utiliza la familia de funciones geom_xx(); por ejemplo geom_point() si queremos puntos, geom_line() si queremos que las relaciones entre las variables se representen/visualicen como lineas.
AsĆ en abstracto puede ser complicado entender del todo que quiere decir todo esto. Vamos a verlo con ejemplos concretos. De momento, para explicar las principales caracterĆsticas de ggplot2 utilizarĆ© un conjunto de datos famoso, pero odiado por algunos, por haber sido utilizado en numerosos ejemplos y cursos: el iris dataset.
El conjunto de datos iris contiene datos sobre 150 flores, en concreto sobre 150 lirios. iris tiene 5 variables, 4 de ellas miden la longitud y el ancho del pétalo y sépalo de los 150 lirios. Estas 4 primeras variables son cuantitativas y continuas; mientras que la quinta variable es categórica, indicando la clase o variedad de los lirios, ya que en los datos hay 3 especies distintas de lirios (setosa, versicolor y virginica). Con estos datos, con 3 de sus variables, se ha creado el grÔfico que ves mÔs arriba.
PRIMER GRĆFICO: Para comenzar nuestras andanzas con ggplot2 intentaremos replicar con código R el grĆ”fico de arriba; aunque al principio sólo utilizaremos 2 variables: haremos un grĆ”fico de puntos de la longitud del sĆ©palo frente a la longitud del pĆ©talo.
Hacer un grƔfico con ggplot2 requiere de varias etapas,
la primera de ellas consiste en usar la función ggplot() para inicializar el grÔfico.
en segundo lugar, tendremos que especificar que conjunto de datos usaremos en el grƔfico.
en tercer lugar tendremos que especificar que variables irƔn asociadas a determinados elementos visuales o estƩticos del grƔfico
por Ćŗltimo, en cuarto lugar, tendremos que especificar que tipo de grĆ”fico o geometrĆa usaremos para visualizar las observaciones.
VeƔmoslo mƔs detenidamente.
En ggplot2, para hacer un grĆ”fico se empieza SIEMPRE llamando a la función ggplot(). Si tecleamos ggplot() en la consola o en un script, parece que no ocurre nada, pero tras la llamada a la función ggplot(), R ha creado un objeto, un contenedor para nuestro futuro grĆ”fico. AĆŗn no vemos el grĆ”fico, faltan cosas, pero ya lo hemos inicializado. Si quieres ver el objeto/contenedor que hemos creado con la llamada a ggplot() tienes que asignarle un nombre, asĆ podrĆ”s verlo en la pestaƱa āEnvironmentā de RStudio.
Para verlo tienes que hacer:
my_grafico <- ggplot()
my_grafico, es un objeto R, concretamente una lista con 9 elementos, que tendremos que ir āllenandoā para hacer nuestro grĆ”fico.
Generalmente, dentro de la función ggplot() se suele especificar el conjunto de datos que vas a utilizar para hacer el grÔfico. A diferencia de los grÔficos de R-base, ggplot2 no permite graficar vectores: los datos que se suministran han de ser SIEMPRE data.frames o similares.
ggplot(data = iris)
ggplot(iris)
Ya estƔ, con cualquiera de las 2 instrucciones de arriba, son equivalentes, ya hemos inicializado el grƔfico y le hemos dicho que datos vamos a usar.
Dijimos que para comenzar harĆamos un grĆ”fico de puntos de la variable Sepal.Length frente a Petal.Length; asĆ que tenemos que decirle a ggplot2 que variables de iris queremos visualizar y con que propiedades estĆ©ticas queremos asociar cada variable. Para ello utilizaremos la función aes() dentro de ggplot(). Lo hacemos con la siguiente expresión:
ggplot(iris, aes(x = Sepal.Length, y = Petal.Length))
AĆŗn no vemos el grĆ”fico, pero con aes() le hemos dicho a R/ggplot2 que queremos asociar/mapear la variable Sepal.Length al eje x, y la variable Petal.Length al eje y. FĆjate que, en la pestaƱa de grĆ”ficos de RStudio, ya vemos los ejes del grĆ”fico; ademĆ”s, fĆjate que ggplot2 ya ha calculado para nosotros el rango de las variables para ajustar los ejes.
Al igual que antes, podemos omitir los nombres de las opciones de la función aes(). Esta función puede tener muchos argumentos (veremos algunos), pero los 2 primeros son siempre x (el eje x) y despuĆ©s y (el eje y o vertical de mi grĆ”fico); es decir podrĆamos hacer lo siguiente que es mĆ”s rĆ”pido de teclear.
ggplot(iris, aes(Sepal.Length, Petal.Length))
Con esta instrucción le estamos diciendo a ggplot2 que vamos a hacer un grĆ”fico con los datos del data.frame iris y que vamos a asociar/conectar/mapear la variable Sepal.Length con el eje x, y la variable Petal.Length con el eje y del grĆ”fico. Perfecto, pero entonces Āæpor quĆ© no vemos el grĆ”fico? La razón estriba en que no le hemos dicho a ggplot2 quĆ© tipo de grĆ”fico queremos (de puntos, de lineas etcā¦). El tipo de grĆ”fico se explicita con una familia de funciones: geom_xx() o geometrĆas. Hay muchas geometrĆas o tipos de grĆ”ficos que podemos usar. Ya lo veremos!!! Nosotros queremos hacer un grĆ”fico de puntos, asĆ que, de la familia de geometrĆas tenemos que usar geom_point(). VeĆ”moslo.
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point()

Como vemos los puntos del grƔfico se visualizan en color negro, con un tamaƱo, una transparencia y una forma determinadas. Obviamente todo esto se puede cambiar dentro de geom_point() con las opciones adecuadas. Por ejemplo:
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point(color = "red", size = 2, alpha = 0.2)

Ya casi estÔ. No es tan complicado. Ya hemos visto las ideas fundamentales de la visualización con ggplot2:
Los grÔficos se inician con la función ggplot(). Generalmente aquà se especifican los datos (data.frame) que queremos utilizar
La función aes() sirve para asociar/mappear variables con atributos/caracterĆsticas estĆ©ticas del grĆ”fico. De hecho el nombre del función aes() viene de aesthetics. Las aesthetics mĆ”s importantes de un grĆ”fico suelen ser los ejes x e y, por eso se ponen siempre al principio de aes(); es decir, son los 2 primeros argumentos de aes(). En nuestro ejemplo hemos asociado la variable Sepal.Length con el eje x, y la variable Petal.Length con el eje y. Veremos mĆ”s aesthetics, como por ejemplo el color o el tamaƱo (de los puntos ⦠o de las lineas o ā¦)
Con geom_**() elegimos el tipo o geometrĆa de grĆ”fico. Hay muchos tipos de grĆ”ficos, asĆ que habrĆ”n muchos geoms. Por ejemplo: geom_point(), geom_line(), geom_ ā¦
Estas 3 ideas son las principales para entender ggplot2. DespuĆ©s hay muchas mĆ”s opciones y elementos que serĆ”n muy importantes para conseguir un buen grĆ”fico, pero en cierta forma son secundarias; por ejemplo, los tĆtulos, los ejes, las escalas, el tema etc⦠lo iremos viendo poco a poco.
Afiancemos las ideas principales de la visualización con ggplot2. ¿Piensa que harÔ la siguiente linea de código?
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_line()
La primera parte de la instrucción es igual a la anterior: queremos un grÔfico con el data.frame iris y queremos asociar la variable Sepal.Length con el eje x, con la propiedad aesthetic eje x y Petal.Length con el eje y; pero le hemos pedido un grÔfico de lineas (geom_line()). En este caso hacer un grÔfico de lineas no tiene mucho sentido, pero si se lo pedimos a R, este nos hace caso y nos lo muestra.

Ya dije que una de las caracterĆsticas importantes de ggplot2 es que funciona por capas que se van superponiendo; para ir aƱadiendo capas a nuestro grĆ”fico tenemos que usar el sĆmbolo +. Por ejemplo si quisiĆ©ramos ver los puntos y las lineas ĀæCómo lo hacemos?
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_line()

Tampoco son muy útiles la lineas en este grÔfico.
Otra geometrĆa o geom_() que se usa mucho es geom_smooth(). ProbĆ©mosla:
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_smooth()

Del grÔfico puede inferirse que hay una relación, no lineal, pero sà directa o positiva entre la longitud del sépalo y del pétalo; pero también se aprecia que hay al menos dos grupos distintos de lirios. Hay un grupo de observaciones cuyo pétalo parece ser claramente menor que el del resto de lirios. Veamos si esto se debe o esta asociado al tipo de lirio, recuerda que hay 3 tipos de lirios, asociados a la variable iris$Species. Para verlo en nuestro grÔfico lo que vamos a hacer es asociar/mapear la variable Species con la aesthetics color:
ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point()

Pues parece que sĆ, que la especie de lirios āsetosaā es mĆ”s pequeƱa, al menos en longitud, del sĆ©palo, pero sobre todo del pĆ©talo.
La variable Species podrĆamos haberla asociado a la estĆ©tica tamaƱo (size), o a la estĆ©tica forma (shape) pero no serĆa tan Ćŗtil ni quedarĆa tan bonito el grĆ”fico. FĆjate que incluso R nos avisa de que asociar la propiedad o aesthetic tamaƱo con una variable categórica como Species no es muy recomendable.
ggplot(iris, aes(Sepal.Length, Petal.Length, size = Species)) + geom_point()
#> Warning: Using size for a discrete variable is not advised.

TambiĆ©n podemos asociar la variable Species a la estĆ©tica āformaā(shape). Lo hacemos en la expresión de mĆ”s abajo. Como veis, ahora las diferencias entre especies de lirios no se aprecian tan bien como cuando usĆ”bamos color = Species
ggplot(iris, aes(Sepal.Length, Petal.Length, shape = Species)) + geom_point()

Os va a costar hacer grƔficos, normal!!!, pero espero que la idea principal ya la tengƔis. Lo que pasa es que no os he contado todo, en realidad es un poco mƔs complejo y versƔtil. Lo medio explico en el siguiente apartado.
MƔs ideas sobre ggplot2
Ya tenĆ©is las ideas principales para hacer grĆ”ficos con ggplot2, pero no os lo he contado todo, tampoco lo voy a hacer ahora, pero si contarĆ© las cosas de una forma diferente para que tengĆ”is mĆ”s flexibilidad/versatilidad a la hora de hacer grĆ”ficos. Si querĆ©is saber toda la verdad tendrĆ©is que ir al libro de Hadley, concretamente aquĆ.
La forma que os he contado de hacer grĆ”ficos ggplot2 es la que verĆ©is habitualmente, yo tambiĆ©n hago mis grĆ”ficos asĆ, solo que para entender mejor el funcionamiento, la sintaxis de ggplot2, os lo tengo que contar otra vez de una forma un poco diferente o ampliada.
Un grĆ”fico de ggplot2 se inicia llamando a la función ggplot() eso es cierto y tambiĆ©n es verdad que generalmente dentro de ggplot() se indica el data.frame que vas a utilizar y con aes() que variables vas a usar y con que elementos visuales o estĆ©ticos quieres asociar cada una de la variables que vas a utilizar. Correcto, pero ā¦. en realidad un grĆ”fico ggplot2 se hace por capas, cada capa se especifica con una función de la familia geom_xx(), asĆ que en realidad los datos y las aes() se ādeberĆanā especificar dentro de la función geom_xx()`.
Parece un poco de lĆo pero en cuanto lo entiendas es muy fĆ”cil y te puede dar mĆ”s flexibilidad a la hora de hacer tus grĆ”ficos. Empecemos: Āærecuerdas que hacen las lineas/expresiones de abajo?
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point()
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_line()
Podemos pensar que las funciones que hacen la representación grĆ”fica realmente son las geom_xx(); es ahĆ donde deberĆamos especificar los datos y variables/estĆ©ticas que queremos usar, pero si no las especificamos en la función geom(), entonces, ggplot2 mirarĆ” a ver si existen, si estĆ”n especificados, dentro de ggplot().
Ahora que ya sabemos el funcionamiento bĆ”sico de ggplot2, veamos algunos detalles mediante algunos ejemplos. Hasta ahora hemos especificado el data.frame que queremos graficar con ggplot(data = my_df) o con ggplot(my_df) y las variables que queremos ver, y a que propiedad estĆ©tica queremos asociarla, con la función aes() dentro de ggplot(). Si lo hacemos asĆ, todos los geoms_xx() que utilicemos compartirĆ”n el conjunto de datos y las variables/estĆ©ticas a mappear y mostrar; pero a veces, en grĆ”ficos mĆ”s complejos podemos querer hacer que cada geom_xx() muestre datos y/o variables distintas.
Entender que cada geom_xx() puede estar asociado a distintos data.frames y/o variables es importante para tener mĆ”s versatilidad con ggplot2.Por ejemplo, las siguientes tres expresiones hacen el mismo grĆ”fico. Se suele utilizar la primera expresión, pero la segunda y tercera expresiones son āmĆ”s flexiblesā, aunque es verdad que si sólo se utiliza un geom_xx() no ganamos nada por usar la segunda o tercera expresión, pero no serĆ” el caso si en nuestro grĆ”fico necesitamos usar varios geom_xx()
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point()
ggplot(iris) + geom_point(aes(Sepal.Length, Petal.Length))
ggplot() + geom_point(data = iris, aes(Sepal.Length, Petal.Length))
FĆjate, como detalle, pero importante, que si utilizas la tercera expresión; es decir, si especificas los datos dentro de la función geom_xx(), es necesario poner el nombre del argumento ; es decir, debes poner data = iris, no puedes poner solo iris. Yo me olvido siempre de este detalle.

En este caso (como el grÔfico solo tiene una capa, como sólo usamos un geom(_xx)) no ganamos nada por usar la segunda o tercera expresión; PERO, cuando usemos varios geom_xx() esto nos darÔ muchas posibilidades para nuestro grÔfico.
Intenta descubrir las diferencias y funcionamiento de las 3 siguientes instrucciones. Recuerda que puedes correr las instrucciones en R para ver que hacen exactamente.
ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point() + geom_smooth()
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point(aes(color = Species)) + geom_smooth()
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_smooth(aes(color = Species))
VeƔmoslas una a una:
- Los datos y las 3 variables/estƩticas dentro de ggplot()
ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point() + geom_smooth()

En este caso los 2 geoms comparten el conjunto de datos (iris) y las variables/estƩticas a graficar
- Los datos y 2 variables/estƩticas dentro de ggplot(), pero una tercera variable (Species), asociada a la estƩtica color, aparece solamente en
geom_point()
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point(aes(color = Species)) + geom_smooth()

- Los datos y 2 variables/estƩticas dentro de ggplot(), pero una tercera variable (Species), asociada a la estƩtica color, aparece solamente en
geom_smooth()
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_smooth(aes(color = Species))

Espero, seguro!!, que te has dado cuenta de que si especificas el data.frame y las variables/estĆ©ticas dentro de ggplot() esto afectarĆ” a todos los geoms del grĆ”fico; pero lo que se especifique dentro de un geom_xx() solo afecta a esa geometrĆa.
Otro ejemplo para entenderlo, ¿por qué no funciona la siguiente expresión?
ggplot(iris) + geom_point(aes(Sepal.Length, Petal.Length)) + geom_smooth(aes(color = Species))
Pues porque para poder representar la linea suavizada se utiliza geom_smooth(), y geom_smooth() necesita como mĆnimo tener variables asociadas a las estĆ©ticas x e y. Como veis, dentro de geom_smooth() solo hemos especificado la estĆ©tica ācolorā y tampoco hemos especificado en la función ggplot() que variables se asocian con x e y. Por lo tanto, geom_smooth() no puede hacer su trabajo, le faltan los ādatosā de x e y para calcular/obtener la linea suavizada.
Vamos con otros ejemplos. La siguientes expresiones tampoco funcionarÔn si intentÔis correrlas en vuestro ordenador. ¿Por qué?
ggplot() + geom_point(data = iris, aes(Sepal.Length, Petal.Length)) + geom_line(aes(Sepal.Length, Petal.Length))
ggplot(aes(Sepal.Length, Petal.Length)) + geom_point(data = iris) + geom_line()
ggplot() + geom_point(data = iris, aes(Sepal.Length, Petal.Length)) + geom_line()
Otro ejemplo: hagamos algo mÔs marciano/complicado. Supón que quieres hacer un grÔfico diferenciando los puntos por color para las tres especies de lirios, pero quieres que solo se vea la linea suavizada para las dos especies mÔs grandes (virginica y versicolor). Los lirios mÔs pequeños son los de la clase setosa. Igual se puede hacer de otra forma pero la que me viene a la cabeza es hacer lo siguiente:
Primero, crear un dataset que sólo contenga a los lirios grandes, los de las especies virginica y versicolor.
iris2 <- iris %>% filter(Species != "setosa") #- me quedo con los lirios que no son de clase "setosa"
Para despuƩs hacer el grƔfico con cualquiera de las 2 expresiones siguientes. Prefiero la segunda porque hay que teclear/escribir menos, pero puede que sea mƔs didƔctica la primera.
ggplot() + geom_point(data = iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_smooth(data = iris2, aes(Sepal.Length, Petal.Length) )
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point(aes(color = Species)) + geom_smooth(data = iris2)
Otro ejemplo mÔs: ¿y si quisiéramos que las 2 especies grandes se representen con el mismo color? Hay varias soluciones, una de las mÔs marcianas es la que propongo abajo. Es una solución rara, pero creo que os ayudarÔ a entender ggplot2
Primero voy a crear un nuevo data.frame sólo con las observaciones de los lirios pequeños, los de la clase setosa.
iris_setosa <- iris %>% filter(Species == "setosa") #- me quedo con los lirios pequeƱos, los de clase "setosa"
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_point(data = iris_setosa, aes(color = Species)) + geom_smooth(data = iris2,aes(Sepal.Length, Petal.Length) )

Otra solución, quizÔs mÔs lógica, consiste en primero agrupar las 2 especies de lirios grandes (versicolor y virginica) en una sola clase.
iris_solo_2_clases <- iris %>% mutate(Species_2 = ifelse(Species %in% c("versicolor", "virginica"), "versi_virgi", "setosa"))
Para después hacer el grÔfico. AdemÔs el grÔfico lo podemos hacer al menos de 2 maneras, la segunda mucho mejor, la primera expresión es un poco enrevesada:
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_point(data = iris_setosa, aes(color = Species)) + geom_smooth(data = iris_solo_2_clases,aes(Sepal.Length, Petal.Length, color = Species_2) )
ggplot(iris_solo_2_clases, aes(Sepal.Length, Petal.Length, color = Species_2)) + geom_point() + geom_smooth()

Como veis, en ggplot2 hay varias maneras de hacer el mismo grƔfico. Esto al principio puede abrumar/molestar, pero muestra la flexibilidad de la sintaxis.
Para ir acabando con la āfilosofĆaā/sintaxis/gramĆ”tica de ggplot2 intenta imaginar que grĆ”ficos hacen las 6 expresiones de mĆ”s abajo.
Si no puedes, recuerda que siempre puedes ejecutar las ordenes en el ordenador. FĆjate sobre todo en la tercera expresión
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_smooth()
ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point() + geom_smooth()
ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point(color = "purple") + geom_smooth()
ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point() + geom_smooth(color = "brown")
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_smooth(aes(color = Species))
ggplot(iris) + geom_point(aes(Sepal.Length, Petal.Length, color = Species) ) + geom_smooth(aes(Sepal.Length, Petal.Length, color = Species))

En la tercera expresión se especifica color = Species dentro de aes() en ggplot(), asĆ que, de momento, todos los geoms del grĆ”fico deberĆan diferenciar por especies de lirios usando el color, PERO, despuĆ©s se vuelve a usar el argumento color dentro de geom_point(), pero fĆjate que no va dentro de de aes(), va fuera. Concretamente hacemos lo siguiente: geom_point(color = "purple"); es decir, para la capa de puntos, y solo para la capa de puntos que se crea con geom_point(), estamos asociando la estĆ©tica color, no a una variable, sino a un color fijo. Sin embargo, para la otra capa del grĆ”fico, la que resulta de usar geom_smooth() sigue siendo valido que la estĆ©tica color estĆ” asociada a la variable Species.
Un detalle: imagina que en un grĆ”fico en el que has fijado 3 estĆ©ticas dentro de ggplot(aes()). En principio las 3 estĆ©ticas afectarĆ”n a todos los geom_xx() que utilices en tu grĆ”fico, pero si quisieras que, por ejemplo, la estĆ©tica color no afectase a un geom concreto, podrĆas hacer lo siguiente: geom_xx(aes(color = NULL)).
Como puedes imaginar, aĆŗn tenemos que ver mĆ”s elementos de ggplot2. Como mĆnimo los tĆtulos y leyendas, los ejes, el tema, coordenadas, etc⦠vamos a ello!!
3. Elementos de un ggplot
Ya hemos presentado los principales elementos de los grÔficos hechos con ggplot2, los que tienen que ver con la representación de las variables. Pero es evidente que un grÔfico tiene muchos mÔs elementos, y lógicamente hay que conocerlos un poco para poder ajustar los grÔficos a nuestras necesidades y mejorar la calidad de nuestros grÔficos.
Ejemplos de otros elementos son: tĆtulos del grĆ”fico y de los ejes, āthemeā del grĆ”fico, small multiples o faceting, anotaciones etcā¦
En estÔ sección iremos mÔs rÔpido. Se presentarÔn solamente algunos ejemplos, conceptos y/o aclaraciones. Si necesitas profundizar mÔs en estos elementos, puedes acudir a la referencia oficial de ggplot2 o al bokkdown de ggplot2.
Ya dijimos que los grƔficos ggplot se componen de capas o layers. Para nosotros, hasta ahora, una capa estaba compuesta de 3 elementos:
Es evidente que en todos los geoms no se pueden especificar todas las caracterĆsticas estĆ©ticas. Por ejemplo si usas geom_point no podrĆ”s especificar la anchura o el tipo de las lineas, porque no estĆ”s usando lineas sino puntos. Para ver que estĆ©ticas admite cada geom tendrĆ”s que mirar la ayuda de cada geom. Al final de este post tienen un grĆ”fico interactivo con el que se puede ver fĆ”cilmente que caracterĆsticas estĆ©ticas admite cada geom. Por ejemplo geom_bar(), que sirve para hacer grĆ”ficos de barras, no admite mappear variables al eje Y, ya que en el eje Y se visualizan/mapean las frecuencias absolutas o relativas de la variable que se representa en el eje X.
Esto es lo bĆ”sico que hay que saber, pero en realidad, una capa necesita de dos elementos mĆ”s: una stat (o transformación estadĆstica) y una posición. Estos dos Ćŗltimos elementos son necesarios pero la verdad es que podrĆamos seguir haciendo grĆ”ficos con ggplot2 sin conocerlos. ĀæPor quĆ©? Pues porque si en una capa no los especificamos, lo hace ggplot2 por nosotros. Lo ha estado haciendo hasta ahora en todos los grĆ”ficos que llevamos hechos. Pero claro, saber como utilizar estos elementos nos darĆ” mĆ”s flexibilidad a la hora de hacer ggplots.
Generalmente las capas se van aƱadiendo con la familia de funciones geom_xx(), PERO tambiƩn se pueden aƱadir capas con otra familia de funciones stat_xx().
Aparte de estos cinco elementos (datos, aes(), geom, stat y posición) los grÔficos ggplot pueden tener mÔs elementos. VeÔmoslos uno a uno.
3.1 TĆtulos del grĆ”fico
Es evidente que un grĆ”fico para ser efectivo y mostrar su mensaje con claridad debe tener un tĆtulo y/o subtĆtulo ilustrativo y debe mostrar información relevante sobre que variables se grafican los ejes X e Y. Este tipo de elementos pueden modificarse de varias maneras, pero nos centraremos en la función labs().
FĆjate que con la función labs(), de labels, podemos cambiar los tĆtulos del grĆ”fico, de los ejes y tambiĆ©n de las leyendas.
En los tĆtulos (tanto del grĆ”fico, como de los ejes y leyendas) tambiĆ©n se pueden cambiar otras caracterĆsticas; por ejemplo, cambiar el tamaƱo, la fuente o el color, pero eso serĆ” tarea de otra función de ggplot2: del grupo de funciones theme_(). Pero el tema o theme de los grĆ”ficos lo veremos en el siguiente apartado.
Tomemos el siguiente grƔfico como referencia y sobre Ʃl iremos aƱadiendo elementos:
p <- ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point()
p

Con la función labs() podemos aƱadirle un tĆtulo, subtitulo, pie de grĆ”fico o caption. TambiĆ©n podemos cambiar el tĆtulo de los ejes X e Y, asĆ como tambiĆ©n el titulo de la leyenda para color, o para otras estĆ©ticas que utilicemos en el grĆ”fico.
Es suficiente con ver un ejemplo:
p + labs(title = "GrƔfico 1: Longitud del sƩpalo frente al pƩtalo",
subtitle = "(diferenciando por especie de lirio)",
caption = "Datos provenientes del Iris dataset",
x = "Longitud del sƩpalo",
y = "Longitud del pƩtalo",
color = "Especie de lirio")

Si quisieras eliminar completamente los tĆtulos del eje X podrĆas hacerlo en el anterior chunk fijando x = NULL dentro de la función labs().
En lugar de usar la función labs(), también podemos utilizar las funciones auxiliares xlab() e ylab()
p + labs(color = NULL, x = NULL) #- borra el tĆtulo de la leyenda y del eje X
p + xlab(NULL) + ylab(NULL) #- elimina tĆtulos de los ejes X e Y

3.2 Themes
Themes control the display of all non-data elements of the plot. You can override all settings with a complete theme like theme_bw(), or choose to tweak individual settings by using theme() and the element_ functions. Use theme_set() to modify the active theme, affecting all future plots.
Para cambiar detalles de la apariencia del grĆ”fico como el tamaƱo, fuentes y color de los tĆtulos, pero tambiĆ©n de los puntos, las lineas, el fondo del grĆ”fico, la apariencia de las grid-lines, el lugar para las leyendas, etc⦠etc⦠contamos con las āfunciones de temaā; todas ellas comienzan con theme_()
En general con las funciones theme_() podemos cambiar/ajustar cualquier elemento del grÔfico, con la excepción de la propia representación de los datos (ya sabemos que esto se hacen con las funciones geom_()). Estos elementos afectan a la apariencia y detalles del grÔfico, pero no a la relación entre variables que se muestra realmente en el grÔfico.
Para empezar a entender que hacen las funciones relacionadas con el theme, seƱalar que ggplot2 incorpora un conjunto de ātemasā que podemos utilizar para cambiar la apariencia del grĆ”fico a nuestro gusto. Puedes verlos todos aquĆ. El tema que usa por defecto ggplot2 es theme_gray(). Veamos a los themes en acción:
p + theme_gray() #- tema por defecto
p + theme_light()
p + theme_dark()
p + theme_classic()
p + theme_minimal()
p + theme_void()

El paquete de R ggthemes incorpora una amplia lista de temas adicionales, algunos de ellos tratan de replicar el estilo de corporaciones famosas como The Economist o Stata. En el tutorial no se ven bien los grÔficos porque los he hecho pequeñitos, pero prueba a hacerlos tú mismo y verÔs que son good-looking. Veamos algunos:
library(ggthemes)
p + theme_economist()
p + theme_fivethirtyeight()
p + theme_stata()
p + theme_solarized()


TambiƩn podemos definir un tema propio para que el grƔfico se ajuste los mƔs posible a nuestras preferencias.
# define custom theme
my_theme <- theme(axis.text.x =
element_text(colour = "grey20", size = 12, angle = 90, hjust = 0.5, vjust = 0.5) , axis.text.y = element_text(colour = "grey20", size = 12) ,
text = element_text(size = 16))
p + my_theme

Se puede fijar el tema/theme de los grÔficos con la función theme_set(). Por ejemplo:
theme_set(theme_minimal()) #- un tema concreto
theme_set(theme_minimal() +
theme(axis.text.x=element_blank(), axis.ticks.x=element_blank())) #- un tema modificando algunas opciones, que ele eje x no muestre ticks ni escalas
Si quieres volver al theme por defecto:
theme_set(theme_gray())
Ejemplos de algunos elementos cuya apariencia que se pueden cambiar con theme()
p + theme(legend.position = "none") #- que no aparezca leyenda
p + theme(legend.position = "bottom") #- leyenda abajo
p + theme(legend.direction = "horizontal") #- leyenda horizontal!!
p + theme(legend.title = element_text(size = 22)) #- tĆtulo de la leyenda a 22
p + theme(legend.key.size = unit(2.4, "cm")) #- tamaƱo de los cuadros de la leyenda
p + theme(text = element_text(size = 20, face = "bold")) #- cambiar el tamaƱo de todos los elementos de texto
p + theme(text = element_text(face = "bold")) #- pone en negrita todos los elementos de texto
p + theme(axis.text.x = element_text(colour = "pink", size = 12, angle = 90, hjust = 0.5, vjust = 0.5)) # apariencia de la escala del eje x
p + theme(axis.title.y = element_text(size=25, angle = 45)) #- tamaƱo y angulo del texto del eje Y
p + theme(plot.subtitle = element_text(hjust = 3)) #- posición horizontal del subtitulo (si lo tuviese)
p + theme(plot.caption = element_text(hjust = 3)) #- posición vertical del pie de grÔfico (si lo tuviese)
p + theme(panel.background = element_rect(fill = "green", colour = "pink", linetype = "longdash", size = 3.5))
p + theme(panel.background = element_blank())
p + theme(panel.background = NULL)
p + theme(plot.background = element_rect(fill = "pink", colour = "purple", linetype = "dotted", size = 7))
Si quieres ver todos las caracterĆsticas que controla y que por tanto puedes modificar con theme(), usa la ayuda de la función theme() o ejecuta en R args(theme).
En general, si quieres cambiar algĆŗn elemento de un ggplot, has de hacer theme(elemento = element_text()). Si quieres eliminar por completo algĆŗn elemento del grĆ”fico, por ejemplo las grid-lines del grĆ”fico, harĆas theme(panel.grid = element_blank()).
Evidentemente todo esto es imposible de aprender, sólo tienes que saber que cualquier elemento del grÔfico se puede cambiar y tienes que saber buscar e interpretar la ayuda.
Se muy poco de escalas de color, pero si quieres ver los 657 colores que tienen un nombre en R, ejecuta lo siguiente:
aa <- as.data.frame(colours())
TambiƩn es interesante este paquete que agrupa un conjunto amplio de paletas de colores para usar en R.
Por último, no sé si conocéis el webcomic XKCD. Pues en R también hay un paquete y un theme para hacer grÔficos al estilo XKCD. Es el paquete xkcd cuyo autor es Emilio Torres-Manzanera de la Universidad de Oviedo. Aquà podéis ver algunos grÔficos hechos con este estilo en R.
IntentĆ© hacer un grĆ”fico con su theme, pero desafortunadamente no me salió; pero justo al dĆa siguiente vi este tweet que hace algo parecido con datos de los Simpsons y su código sĆ me ha funcionado, ademĆ”s simula/construye el estilo XKCD desde cero.
AdemÔs, después vi que Evangelyne Reinolds hizo esta maravilla. Lo haremos en clase?! En este post puedes encontrar el código para reproducir una de las historias o viñetas de XKCD.
3.3 Small multiples o Facetting
El sistema grĆ”fico de ggplot2 incorpora una tĆ©cnica especial llamada āfacetingā que permite dividir un grĆ”fico en mĆŗltiples grĆ”ficos. Cada uno de esos mĆŗltiples grĆ”ficos se realiza sólo para las observaciones de una de los valores de una variable categórica (o factor) incluido en el conjunto de datos. Es mĆ”s fĆ”cil hacerlo que explicarlo/escribirlo.
Por ejemplo, en iris tenemos la variable Species, que es categórica. Lo que se hace con el āfacettingā es dividir el dataset en grupos y hacer el mismo grĆ”fico para cada uno de los grupos. Los grupos se van a definir en función de los valores de la variable Species. Recuerda que hay tres tipos o especies de lirios.
Para hacer un āfacetting graphā podemos usar las funciones facet_wrap() y facet_grid().
Por ejemplo, con la función facet_grid() puedes elegir entre hacer los small multiples por filas o por columnas. Empecemos haciendo un facetting por columnas. AdemÔs, con facet_grid() se pueden usar varias sintaxis, pero la que aparece en la cheatsheet actual de ggplot2 y, por tanto, la recomendada es las que ves en la segunda linea:
#p + facet_grid( . ~ Species) # old sintaxis
p + facet_grid(cols = vars(Species)) # grƔficos x columnas, separando por valores de 'Species'

Ahora por filas:
p + facet_grid(rows = vars(Species)) # grƔficos x filas

También podemos utilizar la función facet_wrap(). Esta función reparte los small multiples en una rejilla con forma de matriz.
p + facet_wrap(vars(Species), nrow = 2, ncol = 2) # graf x filas y columnas

Si en el dataset hubiesen dos variables categóricas podrĆamos hacer que una de ellas sirviese para llenar las filas y la otra las columnas.
Como iris sólo tiene una variable categórica (Species) vamos a discretizar una de las variables continuas. Por ejemplo la anchura del pĆ©talo, crearemos una nueva variable dividiendo las observaciones de Petal.Width en 2 categorĆas, por encima y por debajo de la media de su media. AdemĆ”s lo vamos a hacer con R-base y con dplyr::ntile(). Seguro que hay mejores formas, por ejemplo
Con dplyr::ntile()
iris <- iris %>% mutate(new_variable = ntile(Petal.Width, 2))
Con R-base y la función cut():
iris <- iris
iris$new_variable <- cut(iris$Petal.Width,
breaks = c(-Inf, mean(iris$Petal.Width), Inf),
labels = c("debajo-media", "arriba-media"))
Ahora ya tenemos dos variable discreta y podemos hacer que facet_grid() utilice una variable para llenar filas y otra para columnas. Se puede especificar de dos maneras
ggplot(iris) + geom_point( aes(Sepal.Length, Petal.Length, color = Species)) +
facet_grid(rows = vars(new_variable), cols = vars(Species)) # graf x filas y columnas
ggplot(iris) + geom_point( aes(Sepal.Length, Petal.Length, color = Species)) +
facet_grid(new_variable ~ Species)

Como vemos, los lirios de la clase setosa siempre tiene el ancho de su pƩtalo por debajo de la media, y los virginica siempre estƔn por encima de la media.
Ejes de los small multiples
Podemos ajustar las escalas de los ejes para que sean comunes para cada small multiple (la opción por defecto) o dejar que las escalas de cada grĆ”fico varĆen en función del rango de los datos representados:
p + facet_grid(rows = vars(Species)) #- escalas comunes
p + facet_grid(rows = vars(Species), scales = "free") #- las escalas de cada small pueden variar
p + facet_grid(rows = vars(Species), scales = "free_y") #- solo dejamos libre/variar la escala del eje y
Solo muestro el resultado de la segunda expresión:

Un truquito: el argumento margin en ggplot2::facet_grid() añade margenes a los small multiples facilitando la visualización. AdemÔs añade un nuevo small con todas las observaciones.
Puedes probarlo tĆŗ mismo corriendo lo siguiente:
p + facet_grid(rows = vars(Species), margins = TRUE)

Otro truco, esta vez avanzado: Un gist para poner labels a los smalls multiples.
3.4 Anotaciones
Annotations are a special type of layer that donāt inherit global settings from the plot. They are used to add fixed reference data to plots.
Las anotaciones en los grÔficos permiten resaltar algún fenómenos o observaciónes de interés y son importantes a la hora de contar historias (storytelling) con los grÔficos y visualizaciones.
En el entorno ggplot podemos hacer anotaciones en nuestro grƔficos de varias maneras, por ejemplo con annotate(). Aunque conceptualmente, como seƱala Hadley, las anotaciones son metadatos, desde el punto de vista prƔctico se usan los mismas funciones o geoms para manipularlos.
TambiĆ©n existen algunas funciones auxiliares en ggplot2 y en paquetes especĆficos para hacer anotaciones en grĆ”ficos ggplot. Por ejemplo, cuando se hacen anotaciones en un grĆ”fico de puntos es fĆ”cil que las anotaciones caigan unas encima de otras, el paquete ggrepel permite aliviar este problema.
Utilicemos la función annotate(). Por ejemplo, el siguiente chunk hace algunas anotaciones sin mucho sentido pero fÔciles de entender:
Por ejemplo, el siguiente chunk usa annotate() para hacer algunas anotaciones sin mucho sentido, pero fƔciles de entender:
p + annotate(geom = "text", x = 6, y = 2, label = "Una anotación", size = 5) +
annotate("rect", xmin = 6, xmax = 7,ymin = -Inf, ymax = Inf, alpha = 0.2, fill = "pink") +
annotate("segment", x = 5, xend = 7, y = 6, yend = 8, colour = "blue")

Anotaciones de texto en las observaciones
Agregar texto a un grÔfico es una de las formas mÔs comunes de anotación. Por ejemplo, para señalizar e identificar observaciones anómalas. Sin embargo, como señala Hadley, añadir texto no es fÔcil por la forma en la que R maneja las fuentes.
La función principal para el etiquetado de grÔficos es geom_text(). Por ejemplo:
p + geom_text(aes(label = Species))

TambiĆ©n podĆamos haber aƱadido el valor de la longitud del pĆ©talo.
p + geom_text(aes(label = Petal.Length))

Hemos aƱadido a cada observación un texto, proveniente de alguna de las variables de iris. Este grĆ”fico no es muy Ćŗtil, pero la tĆ©cnica sĆ. Imagina que queremos marcar los lirios 45 y 140. Podemos hacer lo siguiente:
iris_x <- iris[c(45, 140),] #- seleccionamos el lirio 45 y el 140 (con R-base!!)
p + geom_text(data = iris_x, aes(label = Species), color = "black", size = 5)

Podemos ajustar la posición y tamaƱo del texto, etc.. Por ejemplo, podemos cambiar la alineación de las anotaciones con con hjust(āleftā, ācenterā, ārightā, āinwardā, āoutwardā) y vjust (ābottomā, āmiddleā, ātopā, āinwardā, āoutwardā).
Lineas
Podemos aƱadir lineas:
p + geom_vline(xintercept = 6)
p + geom_hline(yintercept = 5, size = 1.7, colour = "black", linetype = "dashed")
p + geom_abline(intercept = 0.7, slope = 0.4, size = 2)
3.5 Cambiando los lĆmites de los ejes
Si quieres modificar el recorrido de los ejes, los ālĆmitesā de los ejes, puedes usar lims(). Para los ejes X e Y hay dos funciones auxiliares: xlim() e ylim().
p + lims(color = c("setosa"), x = c(NA,6), y = c(1,8))

p + xlim(c (4, 6)) + ylim(c(NA, 5))

Se puede hasta dar la vuelta a los ejes
p + xlim(c (7, 3)) + ylim(c(NA, 5))

Los limites o dominio del grĆ”fico suelen obtenerse automĆ”ticamente de los datos, pero, otra vez according to Hadley, hay dos razones por las que podemos estar interesados en cambiar los lĆmites del grĆ”fico:
centrarnos en una región especifica del grÔfico
aumentar los lĆmites para que varios grĆ”ficos ajusten sus escalas.
Por ejemplo, si después de hacer un grÔfico quieres centrarte sólo en una parte; es decir, hacer un zoom sobre una parte del grÔfico, tenemos 2 alternativas:
- Borrar los puntos que caen fuera de los limites de lo que quieras que se visualice (si en una escala continua solo quieres usar un lĆmite pon NA):
p + xlim(c(4, 5)) + ylim(c(NA, 5)) #- cuidado, se pueden borrar observaciones
Con este enfoque tienes que tener cuidado, ya que si por ejemplo despuĆ©s utilizar alguna transformación estadĆstica como por ejemplo geom_smooth(), las observaciones eliminadas al ajustar los lĆmites no entrarĆ”n en el cĆ”lculo estadĆstico.
p + geom_smooth(color = "purple")
p + geom_smooth(color = "purple") + xlim(c(4, 5.7)) + ylim(c(1.5, 5)) # deletes points

- Cambiar los lĆmites de los ejes X e Y haciendo un zoom en la región de interĆ©s pero sin eliminar puntos. Esto lo conseguimos con
coord_cartesian().
p + geom_smooth(color = "purple")
p + geom_smooth(color = "purple") + coord_cartesian(xlim = c(4, 5.7), ylim = c(1.5, 5))

3.6 Escalas
Scales control the details of how data values are translated to visual properties. Override the default scales to tweak details like the axis labels or legend keys, or to use a completely different translation from data to aesthetic. labs() and lims() are convenient helpers for the most common adjustments to the labels and limits.
Las escalas permiten leer/interpretar un grÔfico; permiten interpretar los elementos geométricos (por ejemplo en nuestro grÔfico, los puntos) en función de los valores originales de las observaciones. Las escalas son un elemento mÔs de los grÔficos ggplot y se producen/controlan con la familia de funciones scale_xx()
En ggplot2 las escalas o guĆas se producen automĆ”ticamente, no vemos que hagamos nada, pero under the hood se estĆ”n fijando con la familia de funciones sale_xx() que son las que controlan como se mapean los valores de las variables con las propiedades estĆ©ticas de nuestro grĆ”fico (por ejemplo el eje X) de forma que podamos interpretar la posición de los distintos puntos mirando las escalas. Las escalas tambiĆ©n construyen los elementos que permiten leer/interpretar los grĆ”ficos: los ejes y las leyendas.
Como ggplot2 hace el mapeo y genera las escalas y leyendas automƔticamente, en la prƔctica podemos hacer grƔficos sin saber como funcionan y, por tanto, sin saber manipular este elemento de un grƔfico ggplot. Pero si aprendemos a manipular las escalas, esto nos darƔ mƔs flexibilidad a la hora de utilizar ggplot2.
En muchos tipos de datos es importante pararse a pensar cual es la mejor escala para representar las variables. QuizĆ”s sea conveniente cambiar la escala de un eje para distribuir mejor las observaciones en el espacio, o para interpretar mejor las variaciones entre observaciones; por ejemplo la escala logarĆtmica o en porcentajes son a veces mĆ”s apropiadas que las escalas originales.
En realidad, para cada par variable/estĆ©tica representada en un grĆ”fico ggplot es necesaria una escala, y tendrĆa que fijarse con una de las funciones de la familia scale_xx(). Realmente cuando hacemos este grĆ”fico, en el que asociamos 3 variables a 3 propiedades estĆ©ticas con aes(), se necesitarĆa especificar las escalas de las 3 variables:
p <- ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point(aes (color = Species))
Bien, pero entonces ¿por qué no lo hacemos?, ¿por qué no especificamos las escalas? Pues porque lo hace ggplot2 por nosotros. En realidad cuando ejecutamos la expresión anterior, realmente se estÔ haciendo lo siguiente:
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point(aes (color = Species)) +
scale_x_continuous() +
scale_y_continuous() +
scale_color_discrete()
Es decir, ggplot2 asigna a cada variable una escala, a las variables continuas les asigna una escala continua y a las categóricas una escala discreta. PERO, si queremos, si lo consideramos apropiado podrĆamos cambiar la escala. Por ejemplo:
p + scale_y_reverse() + scale_colour_grey()
p + scale_x_sqrt() + scale_y_log10()
p + scale_x_continuous(trans = "log")
p + scale_color_brewer(palette = "Dark2")
Solo represento la primera transformación, en la que se da la vuelta a la escala del eje Y y el color, asociado a la variable Species, pasa a escala de grises:

Labels de los ejes
TambiĆ©n se pueden modificar lo que vemos en los escalas. Antes vamos a modificar los tĆtulos de los ejes y leyendas.
p <- p + labs(x = "Eje X", y = "Eje Y", color = "Leyenda\n para el color")
De momento la escala del eje X varia aproximadamente de 3 a 8. Los tĆtulos de la escala del eje X sólo muestra los valores 5, 6, 7 y 8. Vamos a modificar āel textoā, los nĆŗmeros que se ven y que sirven de guĆa para la escala del eje X:
p + scale_x_continuous(breaks = seq(3, 10, 0.5), limits = c(3, 10))

En el eje Y se ven los nĆŗmeros 2, 4 y 6. Cambiemos los labels de su escala (no del eje Y, sino de la escala del eje Y):
p + scale_y_continuous(breaks = seq(0, 12, 0.25), label = scales::dollar)

TambiƩn se pueden modificar los de la leyenda para el color:
p + scale_color_manual(values = c("purple", "pink", "red2"), name = "Especies\n de lirios")

Si hubiese usado una variable discreta asociada a la estĆ©tica āshapeā, tendrĆamos que usar scale_shape_discrete(), si la variable asociada a shape fuese continua: scale_shape_continuous()
ĀæY si hubiĆ©semos usado una variable continua para la estĆ©tica āfillā? Pues scale_fill_continuous()
Familia de funciones de escala
⢠scale_continuous()
⢠scale_discrete()
⢠scale_ordinal()
⢠scale_manual()
⢠scale_{color/fill}brewer()
⢠scale{color/fill}distiller()
⢠scale{color/fill}_gradient()
3.7 Stats (transformaciones estadĆsticas)
Puede leerse en la web de ggplot2, concretamente aquĆ lo siguiente:
A layer combines data, aesthetic mapping, a geom (geometric object), a stat (statistical transformation), and a position adjustment. Typically, you will create layers using a geom_ function
PERO
A handful of layers are more easily specified with a stat_ function, drawing attention to the statistical transformation rather than the visual appearance. The computed variables can be mapped using stat().
Algunos grĆ”ficos, como los grĆ”ficos de puntos, no requieren del uso de transformaciones estadĆsticas de las observaciones, pero otros grĆ”ficos como rectas o curvas de predicción o como los boxplots o diagramas de caja, sĆ que las necesitan. Podemos usar transformaciones estadĆsticas en grĆ”ficos ggplot con la familia de funciones stat_xx()
Por ejemplo, cuando se hace un diagrama de caja o boxplot, no se representan las observaciones originales, sino que se muestran 5 estadĆsticos resumen de la distribución de los datos; es decir, se utiliza una transformación estadĆstica. Cuando usĆ”bamos geom_smoth() tampoco representĆ”bamos con Ć©l los datos originales, sino una transformación estadĆstica de estos. Concretamente la transformación estadĆstica que utiliza geom_smoth() es genĆ©ricamente un āsmootherā, calcula mediante una rolling-windows la media de y, condicionada a x.
Cada función geom_xx() que utilicemos, en realidad necesita de un stat_xx(), entonces Āæpor quĆ© nunca lo hemos usado/especificado nosotros? Con ggplot2 la razón es casi siempre la misma: porque under the hood ggplot2 hace muchas cosas por nosotros. En concreto, cada vez que usamos un geom_xx() en realidad ggplot2 estĆ” fijando una transformación estadĆstica por defecto por nosotros. Ggplot es un sistema muy completo, pero aĆŗn asĆ, una vez lo entiendes, hacer grĆ”ficos con el es relativamente fĆ”cil y rĆ”pido porque muchas de sus opciones opciones no hace falta especificarlas directamente.
Por ejemplo, Āæcual es la transformación estadĆstica que se usa por defecto en geom_point()? Ninguna, bueno, en realidad usa stat = "identity", pero como lo especifica ggplot2 automĆ”ticamente, nosotros no nos damos cuenta.
Cuando hacĆamos este grĆ”fico:
ggplot(iris, aes(Petal.Length, Sepal.Length)) + geom_point()
En realidad estƔbamos haciendo
ggplot(iris, aes(Petal.Length, Sepal.Length)) + geom_point(stat = "identity")
ĀæQuĆ© transformaciones estadĆsticas podemos hacer cuando usemos geom_point()? Por ejemplo, geom_point(stat = "unique") sólo representarĆa las observaciones Ćŗnicas o NO repetidas. En este caso creo que iris no tiene observaciones repetidas, asĆ si ejecutĆ”is la instrucción de abajo se seguirĆ”n visualizando los 150 lirios.
ggplot(iris, aes(Petal.Length, Sepal.Length)) + geom_point(stat = "unique") #- dejarĆa solo observaciones no repetidas
Podemos consultar las opciones por defecto completas de geom_point() aquĆ
Cada geom_xx() tiene un default statistic, pero podemos cambiarlo y especificar otra stat para adaptarlo a nuestras necesidades. Por ejemplo, the default statistic for geom_bar() is stat_bin() pero podemos usar otras stat_xx. Otra vez parece un trabalenguas, pero cuando lo entiendes es relativamente sencillo.
Por ejemplo, en nuestro grĆ”fico de puntos, podemos usar otras transformaciones estadĆsticas, una de las que mĆ”s sentido tiene es calcular medias móviles con un mĆ©todo de alisado (smoother)
p <- ggplot(iris, aes(Petal.Length, Sepal.Length, color = Species))
p + geom_point(stat = "identity")
p + geom_point(stat = "smooth", method = "auto")
Aunque en este caso es bastante mƔs fƔcil hacerlo con:
p + geom_point() + geom_smooth()
p + geom_point() + stat_smooth()
p + geom_point() + stat_smooth(method = "lm", se = FALSE, size = 1)
p + geom_point() + geom_smooth(method = "lm", se = FALSE, size = 1)
p + geom_point() + geom_smooth(method = "lm", col = "#C42126", se = FALSE, size = 1)
Algunas transformaciones estadĆsticas Ćŗtiles y en quĆ© geoms estĆ”n disponibles:
- stat_bin(): geom_bar(), geom_freqpoly(), geom_histogram()
- stat_bin2d(): geom_bin2d()
- stat_bindot(): geom_dotplot()
- stat_binhex(): geom_hex()
- stat_boxplot(): geom_boxplot()
- stat_contour(): geom_contour()
- stat_quantile(): geom_quantile()
- stat_smooth(): geom_smooth()
- stat_sum(): geom_count()
Es raro que tengamos que usar estas funciones stats_xx() directamente, pero si quieres ver que hacen exactamente conviene consultar la documentación para ver quĆ© hace y cómo se aplica exactamente cada transformación estadĆstica a los datos.
Hay otras funciones stat_xx() que no se pueden utilizar con las funciones geom_xx():
- stat_ecdf(): compute a empirical cumulative distribution plot.
- stat_function(): compute y values from a function of x values.
- stat_summary(): summarise y values at distinct x values.
- stat_summary2d(), stat_summary_hex(): summarise binned values.
- stat_qq(): perform calculations for a quantile-quantile plot.
- stat_spoke(): convert angle and radius to position.
- stat_unique(): remove duplicated rows.
Algunos ejemplos
Veamos algunos ejemplos Ćŗtiles:
- En un diagrama de caja mostrar la media:
Queremos que la media se represente como un punto, entonces usamos geom_point() pero no queremos representar los valores originales, sino la media, asà que dentro de geom usamos la opción stat = summary
ggplot(iris, aes(Species, Sepal.Length)) +
geom_boxplot() +
geom_point(stat = "summary", fun.y = "mean", colour = "red", size = 4)

Se conseguirĆa los mismo usando directamente la función stat_summary() con la opción geom = āpointā. SĆ, en ggplot2 las cosas se pueden hacer de varias maneras!!
ggplot(iris, aes(Species, Sepal.Length)) +
geom_boxplot() +
stat_summary(geom = "point", fun.y = "mean", colour = "red", size = 4)
- Otro ejemplo: el default stat de geom_histogram es stat = ābinā, mostrandonos el nĆŗmero de observaciones en cada bin. Si queremos que nos muestre frecuencias relatĆvas al grupo o bin mĆ”s numeroso:
ggplot(iris, aes(Sepal.Length)) + geom_histogram()
ggplot(iris, aes(Sepal.Length)) + geom_histogram(aes(y = stat(count / max(count))))

Bonus: Con stat_function() podemos dibujar curvas de densidad:
df <- tibble(x = c(-20, 20))
ggplot(df, aes(x = x)) +
stat_function(fun = dnorm, args = list(mean = 0, sd = 5), color = "black") +
stat_function(fun = dnorm, args = list(mean = 0, sd = 1), color = "red") +
stat_function(fun = dnorm, args = list(mean = 0, sd = 3), color = "blue")
3.8 Position adjustments
All layers have a position adjustment that resolves overlapping geoms. Override the default by using the position argument to the geom_ or stat_ function.
Los ajustes de posición afectan a la posición de los elementos de una capa. Los grĆ”ficos en los que mĆ”s se utilizan los ajustes de posición son los grĆ”ficos de barras. Su posición por defecto es position = āstackā. Se pueden cambiar con el argumento geom_bar(position = āxxxxā), aunque si usas las funcionesposition_xx()` tienes mĆ”s flexibilidad:
ggplot(iris , aes(Species)) + geom_bar()
ggplot(mtcars, aes(cyl)) + geom_bar()

Para poder visualizar grƔficos de barras con 2 variables,tenemos que usar otro dataset: mtcars
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar() #- pos
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar(position = "fill")
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar(position = "dodge")
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar(position = position_dodge2(preserve = "single"))

Por ejemplo, podemos modificar la posición por defecto de nuestro grÔfico de puntos con el iris dataset usando dos enfoques:
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_point(position = "jitter", color = "pink")
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_jitter( color = "pink")

geom_jitter(), o alternativamente geom_point(position = "jitter") cambia la posición original de los datos añadiendo un poco de ruido, haciendo que se desplacen un poco. Esta técnica se usa muchos cuando hay muchos datos similares (overplotting).
3.9 Coordenadas
The coordinate system determines how the x and y aesthetics combine to position elements in the plot. The default coordinate system is Cartesian (coord_cartesian()), which can be tweaked with coord_map(), coord_fixed(), coord_flip(), and coord_trans(), or completely replaced with coord_polar()
Por ejemplo: coord_fixed(). En nuestro grĆ”fico, tanto la longitud del pĆ©talo como del sĆ©palo se miden en las mismas unidades, asĆ que su ratio implĆcito es 1 a 1. Cambiemos el ratio de las coordenadas con coord_fixed()
p <- ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point()
p + coord_fixed(ratio = 1/3)
p + coord_fixed(ratio = 3/1)

6. Tipos de grƔficos
En estÔ sección presentaremos algunos ejemplos de algunos de los grÔficos mÔs utilizados en el anÔlisis de datos. Puedes ver listados mÔs completos en:
6.1 Histogramas
Se utilizan para mostrar la distribución de UNA variable continua, por ejemplo la longitud del sépalo (Sepal.Length).
Para hacer histogramas en ggplot2 se utiliza geom_histogram y, a veces, geom_freqpoly(). Los dos geoms trabajan de la misma manera, dividen la variable x en intervalos y cuentan las observaciones en cada intervalo, para mostrarlas en el eje Y. La diferencia entre ellos es que geom_histogram utiliza barras para mostrar el nĆŗmero de observaciones o frecuencia absoluta, mientras que geom_freqpoly() usa lineas.
ggplot(iris, aes(Sepal.Length)) + geom_histogram()

Como ves, lo que hace un histograma es dividir el eje X en intervalos o ābinsā y mostrar en el eje Y el nĆŗmero de observaciones de x que caen en cada ābinā. AdemĆ”s, ggplot2 nos avisa de que por defecto se divide el eje X en 30 intervalos, pero que podemos cambiarlo con bins = my_n_de_bins.
p <- ggplot(iris, aes(Sepal.Length))
p + geom_histogram(bins = 40) + xlab("40 intervalos")
p + geom_histogram(bins = 4) + xlab("Sólo 4 intervalos")

Otra opción interesante es elegir la anchura del intervalo con la opción binwidth.
p <- ggplot(iris, aes(Sepal.Length))
p + geom_histogram(binwidth = 0.1) + xlab("He elegido la anchura = 0.1")
p + geom_histogram(binwidth = 1.1) + xlab("Esta vez la anchura = 1.1")

Si en el eje Y queremos frecuencias relativas o porcentajes en lugar de counts, podemos hacerlo con:
ggplot(iris, aes(Sepal.Length)) +
geom_histogram(aes(y = stat(count) / sum(count)), bins = 10) +
scale_y_continuous(labels = scales::percent)

Se puede mejorar bastante la apariencia del histograma jugando con los colores y opciones:
ggplot(iris, aes(Sepal.Length)) + geom_histogram(bins = 10, color = "black", fill = "tomato")

Si asociamos/mapeamos la variable Species a alguna estĆ©tica, por ejemplo a la estĆ©tica āfillā o relleno de las barras con aes(fill = Species), se visualizarĆ” quĆ© parte de cada intervalo pertenece a cada clase de lirio.
ggplot(iris, aes(Sepal.Length)) + geom_histogram(bins = 10, aes(fill = Species), color = "black")

Evidentemente, también podemos visualizar como cambia la distribución de la anchura del sépalo entre los 3 tipos de lirios si hacemos un small multiple:
p <- ggplot(iris, aes(Sepal.Length, fill = Species)) + geom_histogram(bins = 10, color = "black")
p + facet_grid(cols = vars(Species))

Para mejorar la visualización podemos poner en el fondo el histograma para todos los datos. Lo aprendĆ aquĆ, aunque ahora mismo hay un paquete para implementar esta tĆ©cnica, es el paquete gghighlight.
iris_backgroung <- iris %>% select(-Species)
ggplot(iris, aes(x = Sepal.Length)) +
geom_histogram(data = iris_backgroung, fill = "grey", bins = 15) +
geom_histogram(aes(fill = Species), bins = 15) +
facet_grid(cols = vars(Species))

geom_density()
Una alternativa a los histogramas son los grÔficos de densidad con geom_density(). Pero, según Hadley:
they are harder to interpret since the underlying computations are more complex. They also make assumptions that are not true for all data, namely that the underlying distribution is continuous, unbounded, and smooth.
Como dice Hadley, geom_density() estima la función de densidad, por lo que la estimación depende de una serie de paramĆ©tros como adjust. Hacemos uso de xlim(3, 9) para expandir loslĆmites del eje X.
ggplot(iris, aes(Sepal.Length)) +
geom_density(color = "red", size = 1.2) +
geom_density(color = "blue", size = 1.2, adjust = 3) +
geom_density(color = "black", size = 1.2, adjust = 0.5) + xlim(2, 10)

Podemos asociar la variable Species a la estética fill con aes(fill = Species) para obtener una estimación de la densidad para cada tipo de lirio.
ggplot(iris, aes(Sepal.Length, fill = Species)) + geom_density(position = "stack", alpha = 0.5)

Hay muchas otras posibilidades. Por ejemplo:
ggplot(iris, aes(Sepal.Length, fill = Species)) + geom_density(position = "fill", alpha = 0.5) #- position = "fill"
ggplot(iris, aes(Sepal.Length, stat(count), fill = Species)) + geom_density(position = "stack", alpha = 0.5) #- stat(count)
Muchas veces se suelen hacer los histogramas superponiendo la f. de densidad normal o una estimación de la densidad de x con geom_density() o con geom_line(stat="density"). Para que el grÔfico se vea mejor, ajustaremos el eje X con xlim():
#- calculamos media y desviación tipica de Sepal.Length para luego usarlas para construir la curva normal
media <- mean(iris$Sepal.Length, na.rm = TRUE) #- media de la longitud del sƩpalo
desviacion <- sd(iris$Sepal.Length, na.rm = TRUE) #- desviación
#- hacemos el histograma
p <- ggplot(iris, aes(Sepal.Length)) +
geom_histogram(aes(y=..density..), color="black", fill = "steelblue", alpha = 0.2)
#- le aƱadimos la densiidad estimada y la normal
p + geom_density( color="purple", size = 1) +
stat_function(fun = dnorm, colour = "red", size = 1, args = list(mean = media, sd = desviacion)) +
xlim(c(min(iris$Sepal.Length)-1, 9))

Si necesitas saber mĆ”s cosas sobre los histogramas puedes acudir aquĆ o aquĆ.
Joy Division plots
Hace poco apareció el paquete ggridges y se pusieron de moda estos tipos de grÔficos:
library(ggridges)
ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(aes(fill = Species), alpha = 0.5)

Como se parecen a la mĆtica portada del primer disco de Joy Division, algunos los conocen como Joy Division plots. Abajo os pongo un ejemplo sacado de la vignette del paquete ggridges.
library(viridis)
ggplot(lincoln_weather, aes(x = `Mean Temperature [F]`, y = `Month`, fill = ..x..)) +
geom_density_ridges_gradient(scale = 3, rel_min_height = 0.01) +
scale_fill_viridis(name = "Temp. [F]", option = "C") +
labs(title = 'Temperatures in Lincoln NE in 2016')

6.2 Scatter plot
Scatter plot, grÔfico de puntos o grÔfico X-Y. Se utiliza para mostrar la relación entre DOS variables continuas. Lo tenemos mÔs que visto, ya que es el tipo de grÔfico que hemos utilizado durante todo el tutorial.
ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
geom_point() +
labs(title = "GrƔfico 1: Longitud del sƩpalo frente al pƩtalo",
subtitle = "(diferenciando por especie de lirio)",
caption = "Datos provenientes del Iris dataset",
x = "Longitud del sƩpalo",
y = "Longitud del pƩtalo",
color = "Especie de lirio")

Overplotting
Los grĆ”ficos de puntos se usan habitualmente para mostrar la relación entre dos variables continuas. En conjuntos de datos con muchas observaciones pueden tener un problema de āoverplottingā. Esta situación ocurre cuando unos puntos se dibujan encima de otros, de forma que no se aprecia bien la relación entre las variables
Hay varias formas de tratar este problema. VeƔmoslo con un ejemplo sacado de la web de ggplot2:
set.seed(1234)
df <- data.frame(x = rnorm(2000), y = rnorm(2000)) #- creamos un conjuto de datos con 2000 observaciones y 2 v.
p <- ggplot(df, aes(x, y)) + xlab(NULL) + ylab(NULL)
p + geom_point()
p + geom_point(shape = 1) # Hollow circles
p + geom_point(shape = ".") # Pixel sized
#- For larger datasets with more overplotting, you can use alpha blending (transparency) t
p + geom_point(alpha = 1 / 10)

De forma alternativa, podemos lidiar con el overplotting utilizando otro enfoque que consiste en pensar en el overplotting como un problema de densidad en 2 dimensiones y utilizar un hex-plot. En un hex-plot se representan regiones (generalmente hexagonales) coloreadas en función del número de observaciones que caen en cada región, de forma que es equivalente a un histograma pero en 2d.
p + geom_bin2d()
p + geom_bin2d(bins = 10)
p + geom_hex()
p + geom_hex() + scale_fill_gradient2(low = "#132B43", high = "#56B1F7")

En este post utilizan hex-plots para mostrar las zonas desde donde tiran a canasta los jugadores NBA. En este otro post utilizan una combinación de geom_point(alpha = .2, pch = 15) + geom_density_2d() para analizar las puntuaciones de videojuegos. AdemÔs hace un uso avanzado de las escalas, al menos para mi, para que el grÔfico sea fÔcil de visualizar.
Acaba de aparecer un nuevo geom: geom_pointdensity(). Como dicen en su repo de Github: āA cross between a scatter plot and a 2D density plotā.
#devtools::install_github("LKremer/ggpointdensity")
library(viridis)
library(ggpointdensity)
p + geom_pointdensity(adjust = 7) +
scale_color_viridis()

Si quieres saber mĆ”s cosas sobre cómo hacer scatterplots con ggplot2 puedes hacerlo por ejemplo, aquĆ o aquĆ.
6.3 Boxplot (grƔficos de caja)
Para visualizar una variable cuantitativa se suelen usar los histogramas, pero si lo que quieres es visualizar como varĆan los valores de una variable continua en función de los valores de una variable categórica (Species) se suelen usan boxplots o diagramas de caja. Son Ćŗtiles para comparar diferentes grupos y para identificar outliers.
Para hacer un boxplot o diagrama de caja se utiliza geom_boxplot(). El grĆ”fico muestra 5 estadĆsticos: la mediana con una linea gruesa, el primer y tercer cuartil de los datos con los limites de la caja y el mĆ”ximo y el mĆnimo (los limites de la linea vertical). Adicionalmente, si existen outliers, estos tambiĆ©n se representarĆ”n.
ggplot(iris, aes(x = Species, y = Sepal.Length)) + geom_boxplot()

Se pueden cambiar algunas opciones del grƔfico
p <- ggplot(iris, aes(x = Species, y = Sepal.Length))
p + geom_boxplot(aes(fill = Species), outlier.colour = "purple")

A veces, para mejorar la visualización, conviene rotar los ejes. Podemos hacerlo con coord_flip()
p <- ggplot(iris, aes(x = Species, y = Sepal.Length)) + geom_boxplot(aes(fill = Species))
p + coord_flip()

Para mejorar la visualización podemos incluir las observaciones originales con geom_point(), pero habrĆa mucho overplotting, asĆ que mejor con geom_jitter()
p + geom_jitter(width = 0.15, alpha = 1/4, color = "tomato")

Podemos aƱadir con stats() algĆŗn estadĆstico mĆ”s con stats_xx(), por ejemplo la media con stats_summary:
p + stat_summary(fun.y = "mean", geom = "point", color = "purple", size = 2.5)

Los boxplots resumen la distribución de una variable cuantitativa con sólo cinco nĆŗmeros, proporcionando un resumen Ćŗtil de los datos, pero ocultan la forma de la distribución; por ejemplo, si la distribución fuese bimodal, no lo apreciarĆamos. AdemĆ”s, aunque usemos geom_jitter() para superponer los valores originales, a veces es difĆcil a simple vista inferir la distribución de estos. Por ello hay varias tĆ©cnicas/geoms Ćŗtiles que ayudan a resolver el problema, por ejemplo geom_violin(). Una alternativa al boxplot son los grĆ”ficos de violĆn donde se estima y muestra la forma de la distribución de las observaciones:
p <- ggplot(iris, aes(x = Species, y = Sepal.Length))
p + geom_violin(aes(fill = Species), alpha = 0.6)
p + geom_violin(aes(fill = Species), alpha = 0.6) + geom_jitter(width = 0.15, alpha = 1/4)

En el ejemplo que hemos usado hemos tenido suerte y los tres grupos estaban ordenados, pero otras veces no tendremos tanta suerte, por ejemplo si en lugar de graficar la longitud de sƩpalo queremos visualizar la anchura (Sepal.Width):
ggplot(iris, aes(x = Species, y = Sepal.Width)) + geom_boxplot()

¿Cómo ordenamos los grupos? Por ejemplo podemos ordenarlos de menor a mayor en función de su anchura media del sépalo. Para ello usamos stats::reorder()
ggplot(iris, aes(x = reorder(Species, Sepal.Width, mean), y = Sepal.Width)) + geom_boxplot() +
xlab("De menor a mayor anchura del sƩpalo")

6.4 GrƔficos de barras
Los grĆ”ficos de barras se utilizan para representar una variable categórica, como por ejemplo Species, o variables cuantitativas discretas. Se representan barras verticales proporcionales a los valores de la variable en cada categorĆa o valor. Para crear grĆ”ficos de barras con ggplot2 se usa geom_bar().
Para hacer nuestro primer grĆ”fico de barras no vamos a utilizar iris porque en la Ćŗnica variable categórica (Species) resulta que hay 50 lirios en cada tipo de especie. Usaremos el data.frame mpg del paquete ggplot2 que contiene 234 observaciones sobre distintos modelos de coches y sus caracterĆsticas. Algunos de los ejemplos estĆ”n sacados de la web de ggplot2.
p <- ggplot(mpg, aes(class))
p + geom_bar()
p + geom_bar(fill = "steelblue") + coord_flip()

Como vemos, la variable class es categórica, concretamente tiene 7 grupos o categorĆas de coches. Las barras verticales son proporcionales al nĆŗmero de vehĆculos en cada categorĆa. Con coord_flip() las categorĆas pasan a representarse en el eje Y, haciendo que, generalmente, se visualicen mejor los nombres de las categorĆas.
Si en lugar de tener una tabla de datos, tenemos ya una tabla de frecuencias, tendremos que especificar en aes() que la variable con las frecuencias se mapee/asocie al eje Y: aes(x = variable, y = frecuencias). AdemƔs tendrƔs que usar geom_bar(stat = "identity") o directamente usar geom_col() que ya usa por defecto stat_identity(). VeƔmoslo con un ejemplo:
df <- mpg %>% group_by(class) %>% count
p <- ggplot(df, aes(x = class, y = n))
p + geom_bar(stat = "identity", fill = "steelblue")
# p + geom_col(fill = "steelblue") #- hace exactamente el mismo plot

Distintas posiciones para las barras
Para esta subsección utilizaremos el data.frame mtcars
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar() #- pos
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar(position = "fill")
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar(position = "dodge")
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar(position = position_dodge2(preserve = "single"))

Reordenando las categorĆas
A veces es importante reordenar las barras para mejorar la visualización. Para ello tendremos que usar factores. Por ejemplo, volviendo al mpg dataset:
df <- mpg
df <- df %>% mutate(class = forcats::as_factor(class)) #- convertimos la v. class a factor con la f. as_factor()
df <- df %>% mutate(class = forcats::fct_infreq(class)) #- fct_infreq() los niveles del factor segĆŗn su frecuencia de mayor a menor
p <- ggplot(df, aes(fct_rev(class))) #- fct_rev() ordena los levels de menor a mayor
p + geom_bar(fill = "steelblue") + coord_flip()

Si queremos que en las barras se visualice el nĆŗmero de observaciones o frecuencia absoluta de cada categorĆa:
p + geom_bar(fill = "steelblue") + coord_flip() +
geom_text(stat='count', aes(label = ..count.. ), hjust = -0.15, size = 3.25)

Porcentajes en lugar de counts
Si queremos que las barras representen porcentajes en lugar de numero de casos en cada categorĆa:
p <- ggplot(df, aes(fct_rev(class))) #- fct_rev() ordena los levels de menor a mayor
p + geom_bar(aes( y = (..count..)/sum(..count..)), fill = "steelblue") + coord_flip()

En realidad esto mismo se pude hacer de varias formas. En este post nos muestran 5 formas de hacerlo.
Si quieres saber mĆ”s sobre como hacer grĆ”ficos de barras con ggplot2, puedes hacerlo aquĆ o aquĆ.
6.5 GrƔficos de lineas
Los grĆ”ficos de lineas se usan principalmente para mostrar la evolución de variables en el tiempo. Generalmente la variable que se muestra en el eje X es el tiempo. En EconomĆa estos grĆ”ficos puede que sean los mĆ”s usados y habitualmente nos referimos a ellos como grĆ”ficos temporales.
Podemos simular grƔficos de tiempo con geom_line() y geom_path(). Estos 2 geoms grafican lineas entre dos observaciones de una variable. Por ejemplo:
ggplot(economics, aes(date, uempmed)) + geom_line()

Empecemos usando el conjunto de datos gapminder para mostrar las observaciones de la variable lifeExp para EspaƱa:
library(gapminder)
gapminder %>% filter(country == "Spain") %>%
ggplot(aes(x = year, y = lifeExp)) + geom_line() + geom_point()

Visualicemos ahora 4 series de tiempo, la esperanza de vida en cuatro paĆses europeos. Para poder distinguir las lineas de cada paĆs podemos asociar la variable country a la caracterĆstica estĆ©tica color: aes(color = country)
gapminder %>% filter(country %in% c("Spain", "France", "Norway", "Belgium")) %>%
ggplot(aes(x = year, y = lifeExp, color = country)) + geom_line() + geom_point()

Una tonterĆa pero que puede ser de utilidad para saber cuanta diferencia hay al final del plot. Lo aprendĆ aquĆ
df <- gapminder %>% filter(country %in% c("Spain", "France", "Norway", "Belgium"))
lifeExp_ends <- df %>% group_by(country) %>% top_n(1, year) %>% pull(lifeExp) #- vector con los valores Ćŗltimos de lifeExp
ggplot(df, aes(x = year, y = lifeExp, color = country)) + geom_line() +
scale_y_continuous(sec.axis = sec_axis(~ ., breaks = lifeExp_ends)) + #- sec_axis() especifica un eje secundario
scale_x_continuous(expand = c(0, 0))

El siguiente ejemplo estÔ sacado de este libro aún en construcción. Utiliza el paquete tidyquant para descargar las cotizaciones de las 4 principales empresas tecnológicas, las GAFA. tidyquant tiene muchas funciones interesantes, puedes verlas corriendo tq_transmute_fun_options().
library(tidyquant)
stocks <- c("GOOGL","AMZN","FB","AAPL") #- seleccionamos a las GAFAs
df <- tq_get(stocks, from = as.Date("2013-01-01"), to = as.Date("2013-12-31"))
ggplot(df, aes(date, y = close, color = fct_reorder2(symbol, date, close))) +
geom_line() + xlab("") + ylab("") +
theme(legend.title = element_blank())

Reescalamos para que las series comiencen todas en 100
df <- df %>% group_by(symbol) %>% mutate(rescaled_close = 100*close / close[1])
ggplot(df, aes(date, y = rescaled_close, color = fct_reorder2(symbol, date, rescaled_close))) +
geom_line() + xlab("") + ylab("") +
theme(legend.title = element_blank())

Otro paquete muy Ćŗtil para analizar y descargar datos financieros es quantmod. El ejemplo siguiente lo he sacado de este fantastico libro.
library(quantmod)
today <- Sys.Date()
three_months_ago <- seq(today, length = 2, by = "-3 months")[2]
getSymbols("AAPL", from = three_months_ago, to = today)
#> [1] "AAPL"
candleChart(AAPL, theme = 'white', type = 'candles')

Si quieres saber mĆ”s sobre grĆ”ficos de lineas puedes ir aquĆ.
Un poco sobre datos temporales
Para trabajar con datos temporales, R tiene distintos paquetes y estructuras, pero para manipular fechas y datos temporales en el tidyverse tenemos el paquete lubridate. Para una introducción a fechas y tiempo en R aquĆ.
En general, para realizar anĆ”lisis estadĆsticos en R con series de tiempo se necesita que los datos estĆ©n en matrices, pero nosotros en el curso estamos trabajando con el tidyverse, y la la estructura de datos usada son los dataframes o tibbles, esto era un problema, pero recientemente, el paquete tsibble ha extendido el tidyverse y las tibbles a los datos temporales, creando una nueva estructura de datos: las ātsibblesā.
ApoyĆ”ndose en esta nueva estructura de datos, las ātsibblesā, el paquete feast proporciona las herramientas y funciones necesarias para trabajar con series temporales en un entorno tidy. feast es un acrónimo de Feature Extraction And Statistics for Time Series. Para una introducción a feast puedes ir aquĆ o aquĆ.
Asimismo, el paquete fable tambiĆ©n utiliza ātsibblesā y proporciona herramientas para la predicción de series temporales, incluyendo modelos ARIMA en el entorno tidyverse. Para una introducción a fable puedes ir aquĆ.
AquĆ puedes ver una conferencia en la que se explica las principales ideas de esta nueva forma de trabajar con series temporales en el tidyverse
7. MƔs detalles/cosas
7.1 geom_smooth()
Ya hemos usado geom_smooth(). Un argumento importante de geom_smooth() es method con el que se selecciona el mĆ©todo con el que se obtiene la smooth curve. El mĆ©todo por defecto es method = "loess". Puedes controlar el nivel de smoothing con el parametro āspanā, que va de 0 to 1 (mayor suavizado).
Otras opciones para geom_smooth():
method = ālmā fits a linear model, giving the line of best fit.
method = ārlmā works like lm(), but uses a robust fitting algorithm so that outliers donāt affect the fit as much. Itās part of the MASS package, so remember to load that first.
stat_smooth(se = FALSE) , indica si se representan intervalos de confianza para la linea suavizada.
AdemĆ”s de los mĆ©todos implementados, podemos elegir nuestro propio mĆ©todo, ya sea usando el argumento āformulaā o definiendo nuestro propio mĆ©todo de alisado como nos cuentan aquĆ
p <- ggplot(iris, aes(Sepal.Length, Petal.Length)) +
geom_point()
p + geom_smooth(method = "lm", formula = y ~ poly(x,4))

Podemos, obviamente, comparar dos mƩtodos de alisado. Para poner nombre a los diferentes mƩtodos se puede hacer lo siguiente:
library(tidyverse)
p <- ggplot(iris, aes(Sepal.Length, Petal.Length)) +
geom_point()
p + geom_smooth(aes(color = "loess") , method = "loess", se = FALSE) +
geom_smooth(aes(color = "lm") , method = "lm" , se = FALSE)

7.2 Ćreas bajo la curva
Para un profesor de estadĆstica/econometrĆa es importante saber hacer grĆ”ficos como estos:
x <- as.data.frame(c(-2, 2))
ggplot(x, aes(x)) +
stat_function(fun = function(x) { x**3 },
geom = "line")
ggplot(NULL, aes(x = c(-20, 20))) +
stat_function(fun = function(x) { x**3 },
geom = "line")
ggplot(NULL, aes(x = c(-3, 3))) +
stat_function(fun = dnorm,
geom = "line")
ggplot(NULL, aes(x = c(-5, 5))) +
stat_function(fun = dnorm, geom = "line", xlim = c(-4, 0)) +
stat_function(fun = dnorm, geom = "area", fill = "steelblue", xlim = c(0, 4)) +
xlim(-5, 5)
Lo aprendĆ aquĆ.
7.3 Labelling las observaciones con ggrepel
El grÔfico de abajo se basa en este post de Julia Silge. Utiliza el paquete `ggrepel para poder ver a quien pertenece la observación en un grÔfico de puntos.
library(ggrepel)
df <- gapminder::gapminder %>% filter(year == "2007") %>% filter(continent == "Europe")
ggplot(df, aes(gdpPercap, lifeExp, label = country)) + geom_point() +
labs(title = "GrƔfico 1: Esperanza de vida frente a PIB per cƔpita" ,
caption = "Datos provenientes de gapminder",
y = "lifeExp",
x = "gdpPercap") + geom_smooth() +
geom_label_repel()

7.4 GGally package
Hay muchos paquetes que proporcionan formas rƔpidas de hacer grƔficos como por ejemplo este del paquete GGally:
library(GGally)
ggpairs(iris)
ggpairs(iris %>% select(1:4) %>% na.omit(), progress = FALSE, lower = list(combo = wrap("facethist", bins=6)))


7.5 Un poco mƔs de anotaciones
Para que un grĆ”fico sea efectivo y nos haga ver algĆŗn hecho o caracterĆstica de los datos, muchas veces es preciso hacer anotaciones en el grĆ”fico para seƱalar o resaltar ciertos aspectos de este. En esta subsección presento algĆŗn ejemplo de uso de las anotaciones en grĆ”ficos ggplot.
- En primer lugar un ejemplo de Hadley. Hadley nos dice que no hay nada novedoso excepto el uso de -Inf and Inf como posiciones para referirse a los lĆmites del grĆ”fico, the top and bottom (or left and right) limits of the plot.
presidential <- subset(presidential, start > economics$date[1])
p <- ggplot(economics) + geom_line(aes(date, unemploy)) +
scale_fill_manual(values = c("blue", "red")) +
xlab("date") +
ylab("unemployment")
#- comienzan las anotaciones
p + geom_rect(aes(xmin = start, xmax = end, fill = party),
ymin = -Inf, ymax = Inf, alpha = 0.2, data = presidential) +
geom_vline(aes(xintercept = as.numeric(start)),
data = presidential, colour = "grey50", alpha = 0.5) +
geom_text(aes(x = start, y = 2500, label = name),
data = presidential, size = 3, vjust = 0, hjust = 0, nudge_x = 50)

Una forma común de anotación consiste en marcar o subrayar un conjunto de puntos. Por ejemplo marcar los coches de la marca Subaru en el siguiente grÔfico. Se puede hacer de la siguiente manera:
p <- ggplot(mpg, aes(displ, hwy)) +
geom_point(data = filter(mpg, manufacturer == "subaru"), colour = "orange", size = 3) +
geom_point()
p

Como ves, lo que se ha hecho es superponer una capa con solo las observaciones de Subaru y graficarlas con un punto mƔs grande de lo habitual y con un color llamativo. El problema es que se ve que sean las observaciones de Subaru. Esto se puede resolver de varias maneras usando annotate():
p + annotate(geom = "point", x = 5.5, y = 40, colour = "orange", size = 3) +
annotate(geom = "point", x = 5.5, y = 40) +
annotate(geom = "text", x = 5.6, y = 40, label = "subaru", hjust = "left")

O de esta otra forma:
p + annotate(geom = "curve", x = 4, y = 35, xend = 2.65, yend = 27,
curvature = .3, arrow = arrow(length = unit(2, "mm"))) +
annotate(geom = "text", x = 4.1, y = 35, label = "subaru", hjust = "left")

El paquete ggforce es una extensión a ggplot2 que puede servir para muchas cosas, entre ellas hacer anotaciones o marcas en grÔficos ggplot. Por ejemplo:
ggplot(iris, aes(Sepal.Length, Petal.Length)) +
geom_point(aes(color = Species)) +
ggforce::geom_mark_ellipse(aes(label = Species, group = Species))

Con ggforce podemos hasta simular un zoom:
ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) +
geom_point() +
ggforce::facet_zoom(x = Species == "versicolor")

El paquete/extensión ggforce es pretty awesome. En este post puede verlo en acción haciendo mapas.
Otro enfoque para hacer anotaciones, bueno, en realidad centrase en un grupo de observaciones es utilizar el paquete gghighlight:
df <- gapminder::gapminder %>% filter(continent == "Europe")
ggplot(df, aes(year, lifeExp, group = country)) +
geom_line() +
geom_point() +
gghighlight::gghighlight(country %in% c("Spain", "Portugal"))

ggplot(iris, aes(Sepal.Length, Petal.Length, color = as.factor(Species))) +
geom_point() +
gghighlight::gghighlight() +
facet_wrap(vars(Species))

9. GrƔficos interactivos
Los grĆ”ficos interactivos, como su nombre indica, permite al usuario interactuar con el grĆ”fico, abriĆ©ndose posibilidades como centrarse en parte del grĆ”fico (zooming), highlighting, o mostrar información adicional al pinchar en algĆŗn elemento del grĆ”fico, etc ā¦
En general, JavaScript (JS) es el lenguaje utilizado para hacer grĆ”ficos interactivos con librerĆas como D3, Chart, Plotly, Vis Highcharts, ā¦
Recientemente, el paquete de R htmlwidgets ha facilitado el uso de las librerĆas de JS en R. Actualmente, paquetes de R, como leaflet, DT, dygraphs, networkD3 y muchos otros, utilizan el framework propuesto por htmlwidgets para hacer disponibles los grĆ”ficos interactivos de JS en R.
Para daros cuenta de lo fĆ”cil que es hacer un grĆ”fico interactivo con R usaremos el paquete plotly que hace posible usar la librerĆa plotly.js en R. Con plotly se pueden hacer muchos tipos de grĆ”ficos, pero por ejemplo, permite con una sola linea convertir un grĆ”fico ggplot en interactivo:
library(plotly)
p <- ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point() + geom_smooth()
ggplotly(p)
p1 <- p + facet_grid(cols = vars(Species))
ggplotly(p1)
p <- ggplot(mpg, aes(class)) + geom_bar(fill = "steelblue") + coord_flip()
ggplotly(p)
AquĆ puedes ver un galerĆa de ejemplos de grĆ”ficos interactivos hechos con R. AquĆ un post, ya de 2015, para iniciarse un poco en estos temas.
Hay muchos paquetes que permiten hacer grƔficos interactivos en R, por ejemplo leaflet, que permite hacer mapas interactivos muy fƔcilmente.
library(leaflet)
m <- leaflet()
m <- addTiles(m)
m <- addMarkers(m, lng = 174.768, lat =-36.852, popup = "The birthplace of R")
m
Todos estos paquetes que permiten hacer grƔficos interactivos a travƩs de htmlwidgets se pueden consultar en: http://gallery.htmlwidgets.org/. Dos paquetes que no estƔn en la gallery: Apexcharts y TSplotly.
En Interactive web-based data visualization with R, plotly, and shiny explican mÔs detalladamente como hacer grÔficos interactivos en R. Tendré que releer su sección Saving and embedding HTML para mostrar en el tutorial alguno de los grÔficos dinÔmicos que he hecho, pero ahora tengo prisa, la próxima clase es pronto y empezamos sà o sà ggplot2.
Hadley tambiĆ©n estĆ” escribiendo un bookdown sobre Shiny: Mastering Shiny. En palabras de Hadley: Shiny is a framework for creating web applications using R code. Para ver que significa esto puedes ver la siguiente galerĆa con ejemplos de aplicaciones shiny.
gganimate
gganimate es un paquete que no hace grÔficos dinÔmicos, pero permite animar grÔficos mediante la creación creación de secuencias de grÔficos. Mejor que explicarlo visita su sección de ejemplos en su wiki: https://github.com/thomasp85/gganimate/wiki. Por ejemplo este ejemplo o este ejemplo de jurgol. En este post explican como usarlo.
Un ejemplo con los datos de gapminder
library(tidyverse)
library(gapminder)
library(gganimate)
gapmider_europe <- gapminder %>% filter(continent == "Europe")
ggplot(gapmider_europe, aes(gdpPercap, lifeExp, size = pop, colour = country)) +
geom_point(alpha = 0.7, show.legend = FALSE) +
scale_colour_manual(values = country_colors) +
scale_size(range = c(2, 12)) +
scale_x_log10() +
facet_wrap(~continent) +
# Here comes the gganimate specific bits
labs(title = 'Year: {frame_time}', x = 'GDP per capita', y = 'life expectancy') +
transition_time(year) +
ease_aes('linear')
AquĆ tienes un ejemplo para hacerlo para los cinco continentes.
LS0tCnRpdGxlOiAiVmlzdWFsaXphY2nDs24gY29uIGdncGxvdDIgKFdJUCkiCmF1dGhvcjogIlBlZHJvIEouIFDDqXJleiIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIgJVknKWAiCiNkYXRlOiAiMjAyMC8wMy8wNSAodXBkYXRlZDogYHIgU3lzLkRhdGUoKWApIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNzczogIWV4cHIgaGVyZTo6aGVyZSgiYXNzZXRzIiwgInN0eWxlc19wanAuY3NzIikgIy1odHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy81NjY4MTg3OS9ob3ctdG8tdXNlLWhlcmUtZm9yLXBhdGhzLXRvLWNzcy1iZWZvcmUtYm9keS1hbmQtYWZ0ZXItYm9kCiAgICB0aGVtZTogcGFwZXIKICAgIGhpZ2hsaWdodDogdGV4dG1hdGUgCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMyAKICAgIHRvY19mbG9hdDogCiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgI2NvZGVfZm9sZGluZzogc2hvdwogICAgZGZfcHJpbnQ6IGthYmxlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQpiaWJsaW9ncmFwaHk6ICJgciBoZXJlOjpoZXJlKCdhc3NldHMnLCAnYmlibGlvLmJpYicpYCIgICMtIGpvb29kZXIuIHNpbmdsZSBxdW90ZXMgaHR0cHM6Ly9jb21tdW5pdHkucnN0dWRpby5jb20vdC91c2UtaGVyZS1oZXJlLWZ1bmN0aW9uLWluLXlhbWwtb3B0aW9uLzE4NjY3LzkKLS0tCgoKCgpgYGB7ciBjaHVua19zZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBldmFsID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgY2FjaGUgPSBGQUxTRSwgY2FjaGUucGF0aCA9ICIvY2FjaGVzLyIsIGNvbW1lbnQgPSAiIz4iLAogICAgICAgICAgICAgICAgICAgICAgI2ZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQ9IDcsICAgCiAgICAgICAgICAgICAgICAgICAgICAjb3V0LndpZHRoID0gNywgb3V0LmhlaWdodCA9IDcsCiAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9IFRSVUUsICBmaWcuc2hvdyA9ICJob2xkIiwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hc3AgPSA3LzksIG91dC53aWR0aCA9ICI2MCUiLCBmaWcuYWxpZ24gPSAiY2VudGVyIikKYGBgCgoKCmBgYHtyIG9wdGlvbnNfc2V0dXAsIGVjaG8gPSBGfQpvcHRpb25zKHNjaXBlbiA9IDk5OSkgIy0gcGFyYSBxdWl0YXIgbGEgbm90YWNpb24gY2llbnRpZmljYQpgYGAKCmBgYHtyLCBlY2hvID0gRkFMU0V9CmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoaGVyZSkKbGlicmFyeSh0aWR5dmVyc2UpCiNkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoInRob21hc3A4NS9wYXRjaHdvcmsiKQpsaWJyYXJ5KHBhdGNod29yaykKYGBgCgoKCiMjIDEuIEludHJvZHVjY2nDs24KCkxvcyBncsOhZmljb3MgZSBpbmZvZ3JhZsOtYXMgc29uLCBjYWRhIHZleiBtw6FzLCB1bmEgcGFydGUgaW1wb3J0YW50ZSBkZSBsYSBtYXlvcsOtYSBkZSB0ZXh0b3MgZXNjcml0b3MsIHlhIHNlYW4gZXN0b3MgIHVuIGluZm9ybWUgdMOpY25pY28sIHVuIGFydMOtY3VsbyBkZSBwZXJpw7NkaWNvIG8gdW4gVEZHLiBFbiBlbCBjYXNvIGRlIGxhIGNpZW5jaWEgZGUgZGF0b3MsIGNvbW8gcHVlZGUgdmVyc2UgYWJham8gZW4gbGEgaW5mb2dyYWbDrWEsIGVsIGFuw6FsaXNpcyB5IGxhIGV4cGxvcmFjacOzbiBkZSBsb3MgZGF0b3MgZXMgdW4gcHJvY2VzbyBpdGVyYXRpdm8gZW4gZWwgcXVlIGxhIHZpc3VhbGl6YWNpw7NuIHkgbGEgZ2VuZXJhY2nDs24gZGUgZ3LDoWZpY29zIG9jdXBhIHVuIGx1Z2FyIGRlc3RhY2Fkby4KCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICI3NSUiLCBmaWcuYWxpZ24gPSAiY2VudGVyIiwgZmlnLmNhcCA9ICJSIGZvciBEYXRhIFNjaWVuY2UgKGh0dHA6Ly9yNGRzLmhhZC5jby5uei8pIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1hZ2VuZXMiLCAidHRfMDZfaW1nXzAxX2V4cGxvcmUtdmlzdWFsaWNlLnBuZyIpICkgCmBgYAoKCgpVbm8gZGUgbG9zIGluc3RydW1lbnRvcyB5IHRhcmVhcyBmdW5kYW1lbnRhbGVzIGRlIHVuIGNpZW50w61maWNvIGRlIGRhdG9zIGVzIGxhIGNhcGFjaWRhZCBkZSByZWFsaXphciB2aXN1YWxpemFjaW9uZXMgZGUgZGF0b3MgYXByb3BpYWRhcyB5IGNvbnZpbmNlbnRlcy4gRWwgYW7DoWxpc2lzIGdyw6FmaWNvIG5vIHNvbG8gYXl1ZGEgZW4gbGEgZXhwbG9yYWNpw7NuIHkgY29tcHJlbnNpw7NuIGRlIGxvcyBkYXRvcywgc2lubyBxdWUgZXMgZnVuZGFtZW50YWwgYSBsYSBob3JhIGRlIG1vc3RyYXIgbGFzIHBvc2libGVzIHJlbGFjaW9uZXMgZW50cmUgdmFyaWFibGVzLCBkZXNjdWJyaXIgcmVsYWNpb25lcyBvIHBhdHJvbmVzIG9jdWx0b3MsIHkgZGVzY2FydGFyIG8gc3VnZXJpciBudWV2YXMgcHJlZ3VudGFzIHNvYnJlIGxvcyBkYXRvcy4gCgoKCkVsIGVudG9ybm8gUiB0aWVuZSBkaXZlcnNvcyBzaXN0ZW1hcyBwYXJhIHZpc3VhbGl6YXIgZGF0b3MsIGxvcyBkb3MgbcOhcyB1dGlsaXphZG9zIHNvbiBlbCBzaXN0ZW1hIGdyw6FmaWNvIGRlIFItYmFzZSB5IGVsIGVjb3Npc3RlbWEgYXNvY2lhZG8gYWwgcGFxdWV0ZSBgZ2dwbG90MmAuIFBvciBkaXZlcnNhcyByYXpvbmVzLCBlbiBlbCBjdXJzbyB1c2FyZW1vcyBlbCBlbnRvcm5vIGBnZ3Bsb3QyYCBwYXJhIGhhY2VyIG51ZXN0cm9zIGdyw6FmaWNvczsgZGUgaGVjaG8sIGVuIGxhIGFjdHVhbGlkYWQgYGdncGxvdDJgLCBkYWRhIHN1IHJhcGlkZXogZW4gbGEgaXRlcmFjacOzbiBlbnRyZSBncsOhZmljb3MsIHZlcnNhdGlsaWRhZCB5IGxhIGN1aWRhZGEgZXN0w6l0aWNhIHF1ZSB0aWVuZW4gc3VzIGdyw6FmaWNvcywgc2UgaGEgY29udmVydGlkbywgYWwgbWVub3MgcG9yIGVsIG1vbWVudG8sIGVuIGVsIHNpc3RlbWEgZXN0w6FuZGFyIHBhcmEgaGFjZXIgZ3LDoWZpY29zIGVuIFIuIFBvciBlamVtcGxvLCBbwr9jw7NtbyBjcmXDqWlzIHF1ZSBoYWNlIGxhIEJCQyBzdXMgZ3LDoWZpY29zP10oaHR0cHM6Ly9tZWRpdW0uY29tL2JiYy12aXN1YWwtYW5kLWRhdGEtam91cm5hbGlzbS9ob3ctdGhlLWJiYy12aXN1YWwtYW5kLWRhdGEtam91cm5hbGlzbS10ZWFtLXdvcmtzLXdpdGgtZ3JhcGhpY3MtaW4tci1lZDBiMzU2OTM1MzUpe3RhcmdldD0iX2JsYW5rIn0sIGV2aWRlbnRlbWVudGUgY29uIFIgeSBgZ2dwbG90MmAuIFB1ZWRlcyB2ZXIgdW5vIGRlIHN1cyByZXBvc2l0b3Jpb3MgW2FxdcOtXShodHRwczovL2dpdGh1Yi5jb20vYmJjL2JicGxvdCl7dGFyZ2V0PSJfYmxhbmsifSB5IHN1IGNvb2tib29rIFthcXXDrV0oaHR0cDovL2JpdC5seS9iYmNnZ3Bsb3QyKXt0YXJnZXQ9Il9ibGFuayJ9LiAKCgpDb24gYGdncGxvdDJgIGVzIHNlbmNpbGxvIGhhY2VyIGdyw6FmaWNvcyBjb24gY2FsaWRhZCBwYXJhIHNlciBwdWJsaWNhZG9zIG8gbW9zdHJhZG9zLCBhZGVtw6FzIGRlIHF1ZSwgZGFkYSBzdSBzaW50YXhpcyBtb2R1bGFyLCBoYWNlIHNlbmNpbGxvIGVsIHJldXRpbGl6YXIgbG9zIGdyw6FmaWNvcyBkdXJhbnRlIGVsIHByb2Nlc28gZGUgYW7DoWxpc2lzLiBFbCBwYXF1ZXRlIGBnZ3Bsb3QyYCBmdWUgaW5pY2lhbG1lbnRlIGRlc2Fycm9sbGFkbyBwb3IgW0hhZGxleSBXaWNraGFtXShodHRwOi8vaGFkbGV5Lm56Lyl7dGFyZ2V0PSJfYmxhbmsifSwgcGVybyBhY3R1YWxtZW50ZSBlbCBlY29zaXN0ZW1hIGdncGxvdCBlcyBlbCByZXN1bHRhZG8gZGUgdG9kYSB1bmEgY29tdW5pZGFkIGRlIHVzdWFyaW9zIHF1ZSBjb250cmlidXllIGEgZW5yaXF1ZWNlciBlbCBzaXN0ZW1hIGdyw6FmaWNvIGNvbiBzdXMgZXh0ZW5zaW9uZXMgeSBwYXF1ZXRlcyBhdXhpbGlhcmVzLgoKCkVuIHBhbGFicmFzIGRlIEhhZGxleSBlbiBzdSBbbGlicm8gc29icmUgZ2dwbG90Ml0oaHR0cHM6Ly9nZ3Bsb3QyLWJvb2sub3JnLyl7dGFyZ2V0PSJfYmxhbmsifToKCj4gZ2dwbG90MiBpcyBhbiBSIHBhY2thZ2UgZm9yIHByb2R1Y2luZyBzdGF0aXN0aWNhbCwgb3IgZGF0YSwgZ3JhcGhpY3MsIGJ1dCBpdCBpcyB1bmxpa2UgbW9zdCBvdGhlciBncmFwaGljcyBwYWNrYWdlcyBiZWNhdXNlIGl0IGhhcyBhIGRlZXAgdW5kZXJseWluZyBncmFtbWFyLiBUaGlzIGdyYW1tYXIsIGJhc2VkIG9uIHRoZSBHcmFtbWFyIG9mIEdyYXBoaWNzIChXaWxraW5zb24gMjAwNSksIGlzIG1hZGUgdXAgb2YgYSBzZXQgb2YgaW5kZXBlbmRlbnQgY29tcG9uZW50cyB0aGF0IGNhbiBiZSBjb21wb3NlZCBpbiBtYW55IGRpZmZlcmVudCB3YXlzLiBUaGlzIG1ha2VzIGdncGxvdDIgdmVyeSBwb3dlcmZ1bCBiZWNhdXNlIHlvdSBhcmUgbm90IGxpbWl0ZWQgdG8gYSBzZXQgb2YgcHJlLXNwZWNpZmllZCBncmFwaGljcywgYnV0IHlvdSBjYW4gY3JlYXRlIG5ldyBncmFwaGljcyB0aGF0IGFyZSBwcmVjaXNlbHkgdGFpbG9yZWQgZm9yIHlvdXIgcHJvYmxlbS4gVGhpcyBtYXkgc291bmQgb3ZlcndoZWxtaW5nLCBidXQgKipiZWNhdXNlIHRoZXJlIGlzIGEgc2ltcGxlIHNldCBvZiBjb3JlIHByaW5jaXBsZXMgYW5kIHZlcnkgZmV3IHNwZWNpYWwgY2FzZXMsIGdncGxvdDIgaXMgYWxzbyBlYXN5IHRvIGxlYXJuKiogKGFsdGhvdWdoIGl0IG1heSB0YWtlIGEgbGl0dGxlIHRpbWUgdG8gZm9yZ2V0IHlvdXIgcHJlY29uY2VwdGlvbnMgZnJvbSBvdGhlciBncmFwaGljcyB0b29scykuCgoKU8OtLCBjb24gYGdncGxvdDJgIGVzICJmw6FjaWwiIGhhY2VyIHLDoXBpZGFtZW50ZSBncsOhZmljb3MgZGUgY2FsaWRhZCwgUEVSTyBkb21pbmFyIHRvZG9zIGxvcyBkZXRhbGxlcyBkZWwgcGFxdWV0ZSBzw60gcXVlIGVzIGNvbXBsaWNhZG8sIHBlcm8gbm8gbm9zIGhhY2UgZmFsdGEgY29ub2NlcmxvIHRvZG8uIEFkZW3DoXMsIGhheSBxdWUgdGVuZXIgZW4gY3VlbnRhIHF1ZSBgZ2dwbG90MmAgZXMgdW4gcGFxdWV0ZS9lbnRvcm5vIGVuIGNvbnN0YW50ZSBldm9sdWNpw7NuLiBBY3R1YWxtZW50ZSBlc3TDoSBlbiBsYSB2ZXJzacOzbiAzLjIuMS4gCgoKUGFyYSBlbnRlbmRlciBlc3RhIGlkZWEgZGUgbGEgY29uc3RhbnRlIGV2b2x1Y2nDs24geSBlbCBwYXBlbCBxdWUgdGllbmUgbGEgY29tdW5pZGFkIGRlIHVzdWFyaW9zIGVuIGVsIGRlc2Fycm9sbG8gZGUgUiB5IHN1cyBwYXF1ZXRlcyBwdWVkZXMgbGVlciBbZXN0ZSB0d2VldF0oaHR0cHM6Ly90d2l0dGVyLmNvbS9DbGF1c1dpbGtlL3N0YXR1cy8xMTY2MzU2MjEwNzgzODcwOTc2KXt0YXJnZXQ9Il9ibGFuayJ9IHkgbGFzIHJlc3B1ZXN0YXMgYSDDqWwuIEVuIGVsIHR3ZWV0IHPDs2xvIHNlIGFudW5jaWEgdW5hIHBlcXVlw7FhIG1lam9yYSBlbiBjb21vIGBnZ3Bsb3QyYCBnZXN0aW9uYSBsb3MgdMOtdHVsb3MgZGUgbG9zIGdyw6FmaWNvcyBwZXJvIGdlbmVyYSByZWFjY2lvbmVzIGVuIGxhIGNvbXVuaWRhZCBkZSB1c3Vhcmlvcy4gCgpMYSByZWRlcyBzb2NpYWxlcyBwdWVkZW4gaGFjZXJzZSBlY28gZGUgbGEgZXZvbHVjacOzbiBkZSB1biBwYXF1ZXRlLCBwZXJvIGRvbmRlIGhhYml0dWFsbWVudGUgc2UgcHJvZHVjZSBsYSBkaXNjdXNpw7NuL2NvbGFib3JhY2nDs24gZW50cmUgdXN1YXJpb3MgZXMgZW4gcGxhdGFmb3JtYXMgY29tbyBbR2l0aHViXShodHRwczovL2dpdGh1Yi5jb20vKXt0YXJnZXQ9Il9ibGFuayJ9LiBQb3IgZWplbXBsbywgcHVlZGVzIHZlciBjb21vIHNlIGdlc3TDsyBlc3TDoSBwZXF1ZcOxYSAgbWVqb3JhIFthcXXDrV0oaHR0cHM6Ly9naXRodWIuY29tL3RpZHl2ZXJzZS9nZ3Bsb3QyL2lzc3Vlcy8zMjUyKXt0YXJnZXQ9Il9ibGFuayJ9LiBGdWUgbGEgaXNzdWUgMzI1MiBkZSBgZ2dwbG90MmAuIE90cm8gZWplbXBsbywganVzdG8gY3VhbmRvIGVzdGFiYSBlc2NyaWJpZW5kbyBlc3RlIHDDoXJyYWZvLCBsZcOtIFtlc3RlIG90cm8gdHdlZXRdKGh0dHBzOi8vdHdpdHRlci5jb20vdGhvbWFzcDg1L3N0YXR1cy8xMTY2NjA5NTI5NzcxMTAyMjA4KXt0YXJnZXQ9Il9ibGFuayJ9IGFudW5jaWFuZG8gb3RyYSBtZWpvcmEgZW4gYGdncGxvdDJgLgoKCkxhIHDDoWdpbmEgd2ViIGRlIGBnZ3Bsb3QyYCBwdWVkZXMgZW5jb250cmFybGEgW2FxdcOtXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0uIEVuIGVsbGEgcHVlZGVzIGVuY29udHJhciBkb2N1bWVudG9zIGRlIGF5dWRhIHkgbGEgW3JlZmVyZW5jaWEgb2ZpY2lhbF0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2luZGV4Lmh0bWwpe3RhcmdldD0iX2JsYW5rIn0uIFBhcmEgZGFydGUgY3VlbnRhIGRlIHRvZG8gbG8gcXVlIHNlIHB1ZWRlIGhhY2VyIGNvbiBlbCBlY29zaXN0ZW1hIGdncGxvdCAgdmlzaXRhIFtlc3RhIHDDoWdpbmFdKGh0dHA6Ly93d3cuZ2dwbG90Mi1leHRzLm9yZy9nYWxsZXJ5Lyl7dGFyZ2V0PSJfYmxhbmsifSBkb25kZSBwb2Ryw6FzIHZlciBsb3MgNTggInBhcXVldGVzIGF1eGlsaWFyZXMiIG8gZXh0ZW5zaW9uZXMgYSBgZ2dwbG90MmAuCgoKUGFyYSBoYWNlciAiYnVlbm9zIiBncsOhZmljb3MgY29uIGBnZ3Bsb3QyIGBubyBzw7NsbyBlcyBuZWNlc2FyaW8gZW50ZW5kZXIgbGEgc2ludGF4aXMgeSBsb3MgcG9ybWVub3JlcyBkZWwgcGFxdWV0ZSwgc2lubyBxdWUgcXVpesOhcyBzZSBuZWNlc2l0ZSBhbGdvIG3DoXMuIFBvciBlamVtcGxvLCBhbGdvIGRlIGV4cGVyaWVuY2lhIHkgY2llcnRhIGNhcGFjaWRhZCB2aXN1YWwgeSBlc3TDqXRpY2E7IGluY2x1c28gaGF5IHF1aWVuIGRpY2UgcXVlIGhhY2VyIGJ1ZW5vcyBncsOhZmljb3MgZXMgdW4gYXJ0ZS4gUGFyYSBpbnRlbnRhciBtZWpvcmFyIHZ1ZXN0cm9zIGdyw6FmaWNvcyBvIGV2aXRhciBjaWVydG9zIGVycm9yZXMsIFthcXXDrV0oaHR0cHM6Ly9yb2JqaHluZG1hbi5jb20vaHluZHNpZ2h0L2dyYXBoaWNzLyl7dGFyZ2V0PSJfYmxhbmsifSB0ZW7DqWlzIGFsZ3VuYXMgcmVnbGFzL2NvbnNlam9zLCB5IFthcXXDrV0oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vdGVhY2hpbmctYW5kLWxlYXJuaW5nLW1hdGVyaWFscy1mb3ItZGF0YS12aXN1YWxpemF0aW9uLyl7dGFyZ2V0PSJfYmxhbmsifSB1biBjdXJzbyBjb21wbGV0byBzb2JyZSB2aXN1YWxpemFjacOzbiBjb24gW2Jvb2tkb3duIGluY2x1aWRvXShodHRwczovL3NlcmlhbG1lbnRvci5jb20vZGF0YXZpei8pe3RhcmdldD0iX2JsYW5rIn17dGFyZ2V0PSJfYmxhbmsifV5bRWwgcmVwbyBkb25kZSBzZSBhbG9qYSBlbCBjw7NkaWdvIHBhcmEgcmVjcmVhciBlbCBsaWJybyBlc3TDoSBbYXF1w61dKGh0dHBzOi8vZ2l0aHViLmNvbS9jbGF1c3dpbGtlL2RhdGF2aXopXS4KCk90cm9zIGRvcyBsaWJyb3Mgc29icmUgdmlzdWFsaXphY2nDs24gY29uIGVsIGPDs2RpZ28gZGUgbG9zIGVqZW1wbG9zIGhlY2hvcyBjb24gYGdncGxvdDJgLCBbYXF1w61dKGh0dHBzOi8vc29jdml6LmNvL2luZGV4Lmh0bWwjcHJlZmFjZSkgeSBbYXF1w61dKGh0dHBzOi8vc29jdml6LmNvL2xvb2thdGRhdGEuaHRtbCNsb29rYXRkYXRhKXt0YXJnZXQ9Il9ibGFuayJ9LiBGaW5hbG1lbnRlLCBhbGd1bm9zIGNvbnNlam9zIGRlIGxhIFtCQkNdKGh0dHBzOi8vYmJjLmdpdGh1Yi5pby9yY29va2Jvb2svKXt0YXJnZXQ9Il9ibGFuayJ9IHNvYnJlIHZpc3VhbGl6YWNpw7NuLgoKQ29tbyB0YW1iacOpbiBzZSBhcHJlbmRlIGxvcyBlcnJvcmVzLCBbYXF1w61dKGh0dHBzOi8vbWVkaXVtLmVjb25vbWlzdC5jb20vbWlzdGFrZXMtd2V2ZS1kcmF3bi1hLWZldy04Y2RkOGE0MmQzNjgpe3RhcmdldD0iX2JsYW5rIn0gdGllbmVzIHVuIGFydGljdWxvIGRlIFRoZSBFY29ub21pc3QgZG9uZGUgbXVlc3RyYW4gZXJyb3JlcyBxdWUgZWxsb3MgbWlzbW9zIGhhbiBjb21ldGlkbyBoYWNpZW5kbyBncsOhZmljb3MuIEFkZW3DoXMgcHVlZGVuIGRlc2NhcmdhcnNlIGxvcyBkYXRvcy5PdHJvIGVqZW1wbG8gZGUgZ3LDoWZpY28gdW4gcG9xdWl0byB0ZW5kZW5jaW9zbywgZXN0YSB2ZXogZGVsIFdhc2hpbmd0b24gUG9zdDoKCjxibG9ja3F1b3RlIGNsYXNzPSJ0d2l0dGVyLXR3ZWV0Ij48cCBsYW5nPSJlbiIgZGlyPSJsdHIiPkhleSA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL3dhc2hpbmd0b25wb3N0P3JlZl9zcmM9dHdzcmMlNUV0ZnciPkB3YXNoaW5ndG9ucG9zdDwvYT4gdGhpcyBpcyBub3QgaG93IGJhciBncmFwaHMgd29yayA8YSBocmVmPSJodHRwczovL3QuY28vdVRCS3JwNEhZMiI+cGljLnR3aXR0ZXIuY29tL3VUQktycDRIWTI8L2E+PC9wPiZtZGFzaDsgQW5keSBQcmVzc21hbiAoQGFuZHlwcmVzc21hbikgPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9hbmR5cHJlc3NtYW4vc3RhdHVzLzExMjI1MTI2NjQ4MTM2MDQ4NjU/cmVmX3NyYz10d3NyYyU1RXRmdyI+QXByaWwgMjgsIDIwMTk8L2E+PC9ibG9ja3F1b3RlPiA8c2NyaXB0IGFzeW5jIHNyYz0iaHR0cHM6Ly9wbGF0Zm9ybS50d2l0dGVyLmNvbS93aWRnZXRzLmpzIiBjaGFyc2V0PSJ1dGYtOCI+PC9zY3JpcHQ+IAoKPGJyPgoKIyMgMi4gSWRlYXMgYsOhc2ljYXMgc29icmUgZ2dwbG90MgoKWWEgc2UgZGlqbyBxdWUgYGdncGxvdDJgIGVzIHVuIHBhcXVldGUgUiBkZXNhcnJvbGxhZG8gcG9yIEhhZGxleSBXaWNraGFtLCBhdW5xdWUgYWN0dWFsbWVudGUgZXMgZWwgcmVzdWx0YWRvIGRlIGxhIGNvbGFib3JhY2nDs24gZGUgbcO6bHRpcGxlcyBkZXNhcnJvbGxhZG9yZXMuIGBnZ3Bsb3QyYCBpbXBsZW1lbnRhIGVuIFIgW1RoZSBHcmFtbWFyIG9mIEdyYXBoaWNzXShodHRwczovL3d3dy5zcHJpbmdlci5jb20vZ3AvYm9vay85NzgwMzg3MjQ1NDQ3KSBkZSBMLiBXaWxraW5zb24sIHVuIHNpc3RlbWEgY29oZXJlbnRlIHBhcmEgZGVzY3JpYmlyIHkgY29uc3RydWlyIGdyw6FmaWNvcy4gRWwgw6luZmFzaXMgZGUgZ2dwbG90MiBlc3TDoSBlbiBsYSBleHBsb3JhY2nDs24gcsOhcGlkYSBkZSBkYXRvcywgZXNwZWNpYWxtZW50ZSBkZSBkYXRvcyBkZSBhbHRhIGRpbWVuc2lvbmFsaWRhZC4gQ29uIGBnZ3Bsb3QyYCBlcyBzZW5jaWxsbyBpciB0cmFuc2Zvcm1hbmRvIGVsIGdyw6FmaWNvIG1pZW50cmFzIHNlIHZhbiBhbmFsaXphbmRvIGxvcyBkYXRvcy4gCgoKClBhcmEgZW1wZXphciBhIGVudGVuZGVyIGxhICJmaWxvc29mw61hIiBkZSBgZ2dwbG90MmAsIG9zIHBsYW50ZW8gdW5hIHByZWd1bnRhIG1lZGlvIHJldMOzcmljYTogwr9xdcOpIHZlbW9zIGVuIGVsIGdyw6FmaWNvIGRlIGFiYWpvPwoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICI2MCUifQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBjb2xvciA9IFNwZWNpZXMpKSArIGdlb21fcG9pbnQoKQpgYGAKCgpQdWVzIHPDrSwgZXMgdW4gZ3LDoWZpY28gZGUgcHVudG9zIHkgbm9zIGF5dWRhIGEgdmVyIGxhcyByZWxhY2lvbmVzIHF1ZSBleGlzdGVuIGVudHJlIDMgdmFyaWFibGVzLiBFc3RhbW9zIGhhYml0dWFkb3MgYSBlbGxvLCBwZXJvIHZhbW9zIGEgcGVuc2FyIGVuIGVsIGdyw6FmaWNvIGRlc2RlIGxhIMOzcHRpY2EgZGUgIlRoZSBHcmFtbWFyIG9mIEdyYXBoaWNzIiBpbXBsZW1lbnRhZGEgZW4gZWwgcGFxdWV0ZSBgZ2dwbG90MmAuIAoKRW4gbnVlc3RybyBncsOhZmljbyBzZSByZXByZXNlbnRhbiBwb3IgbWVkaW8gZGUgcHVudG9zLCBlbiBlbCBlc3BhY2lvIFgtWSwgeSBtZWRpYW50ZSBsb3MgZGlzdGludG9zIGNvbG9yZXMgZGUgbG9zIHB1bnRvcywgbGFzIG9ic2VydmFjaW9uZXMgZGUgMyB2YXJpYWJsZXMuIEJpZW4sIG5hZGEgbXV5IG5vdmVkb3NvLCB0b2RvcyBsb3Mgc2lzdGVtYXMgZ3LDoWZpY29zIGhhY2VuIGVzdGUgdGlwbyBkZSBncsOhZmljb3MuIExvcyBncsOhZmljb3MgZGUgYGdncGxvdDJgIHNlIHJlYWxpemFuIG1lZGlhbnRlIGxhIHN1cGVycG9zaWNpw7NuIGRlIGVsZW1lbnRvcy9jYXBhcy4gUG9kZW1vcyBwZW5zYXIgcXVlIGVsIGdyw6FmaWNvIHF1ZSBoZW1vcyB2aXN0byBlcyB1bmEgY2FwYS4gwr9Dw7NtbyBjcmVhbW9zIGVzdGUgZ3LDoWZpY28gbyBjYXBhIGVuIGBnZ3Bsb3QyYD8KClB1ZXMsIHVuYSBkZSBsYXMgcHJpbmNpcGFsZXMgaWRlYXMgcGFyYSBlbnRlbmRlciBgZ2dwbG90MmAgZXMgcXVlIGNhZGEgY2FwYSBkZSB1biBncsOhZmljbyB0aWVuZSAzIGNvbXBvbmVudGVzIG8gZWxlbWVudG9zIHByaW5jaXBhbGVzOgoKCiAgLSAqKmxvcyBkYXRvcyoqIHF1ZSBzZSB2YW4gYSByZXByZXNlbnRhciAgKHNlbmNpbGxvLCBwYXJhIGhhY2VyIHVuIGdyw6FmaWNvIGhhY2VuIGZhbHRhIGRhdG9zKS4gUGFyYSBlbGxvIHV0aWxpemFyZW1vcyBnZW5lcmFsbWVudGUgbGEgZnVuY2nDs24gYGdncGxvdCgpYAogIAogIC0gKip1biBjb25qdW50byBkZSBwcm9waWVkYWRlcyBlc3TDqXRpY2FzIGFzb2NpYWRhcyBhIGFsZ3VuYSB2YXJpYWJsZSBkZWwgY29uanVudG8gZGUgZGF0b3MqKi4gUG9yIGVqZW1wbG8sIGxhIHZhcmlhYmxlIFNlcGFsLkxlbmdodCBlc3TDoSBhc29jaWFkYSBhbCBlamUgWCwgYWxhIHBvc2ljacOzbiBlbiBlbCBlamUgWC4gUG9yIHN1IHBhcnRlLCBlbCBjb2xvciBkZSBsb3MgcHVudG9zIChvdHJhIGNhcmFjdGVyw61zdGljYSB2aXN1YWwgbyBlc3TDqXRpY2EpIGVzdMOhIGFzb2NpYWRvIGEgbG9zIHZhbG9yZXMgZGUgbGEgdmFyaWFibGUgU3BlY2llcy4gRXMgZGVjaXIsIHVzYW5kbyBsYSB0ZXJtaW5vbG9nw61hIGRlIGBnZ3Bsb3QyYCwgbGFzIGRpc3RpbnRhcyB2YXJpYWJsZXMgZXN0w6FuIGFzb2NpYWRhcyBvIG1hcGVhZGFzIGEgZGV0ZXJtaW5hZGFzIGNhcmFjdGVyw61zdGljYXMgZXN0w6l0aWNhcy4gRWwgbWFwZW8gZGUgdmFyaWFibGVzIGNvbiBlc3TDqXRpY2FzIHNlIGhhcsOhIGNvbiBsYSBmdW5jacOzbiBgYWVzKClgICBkZSAqKmFlcyoqdGhldGljcy4gKGVzdG8geWEgbm8gZXMgdGFuIGVzdMOhbmRhciwgc2UgZXhwbGljYSBlbiBicmV2ZSkKICAKICAtICoqZWwgZWxlbWVudG8gZ2VvbcOpdHJpY28gcXVlIHNlIHZhIGEgcmVwcmVzZW50YXIqKi4gRW4gbnVlc3RybyBjYXNvIGVsIGVsZW1lbnRvIGdlb23DqXRyaWNvIHF1ZSBzZSB1dGlsaXphIHBhcmEgcmVwcmVzZW50YXIgbG9zIHZhbG9yZXMgZGUgbGFzIHZhcmlhYmxlcyBzb24gbG9zIHB1bnRvcywgcGVybyBwb2Ryw61hbiBoYWJlciBzaWRvIGxhcyBsaW5lYXMgbyBsYXMgYmFycmFzIC4uLiBQYXJhIGVzcGVjaWZpY2FyIGVsIGVsZW1lbnRvIGdlb23DqXRyaWNvIHF1ZSB2YW1vcyBhIHVzYXIgZW4gbnVlc3RybyBncsOhZmljbyBzZSB1dGlsaXphIGxhIGZhbWlsaWEgZGUgZnVuY2lvbmVzIGBnZW9tX3h4KClgOyBwb3IgZWplbXBsbyBgZ2VvbV9wb2ludCgpYCBzaSBxdWVyZW1vcyBwdW50b3MsIGBnZW9tX2xpbmUoKWAgc2kgcXVlcmVtb3MgcXVlIGxhcyByZWxhY2lvbmVzIGVudHJlIGxhcyB2YXJpYWJsZXMgc2UgcmVwcmVzZW50ZW4vdmlzdWFsaWNlbiBjb21vIGxpbmVhcy4KICAKQXPDrSBlbiBhYnN0cmFjdG8gcHVlZGUgc2VyIGNvbXBsaWNhZG8gZW50ZW5kZXIgZGVsIHRvZG8gcXVlIHF1aWVyZSBkZWNpciB0b2RvIGVzdG8uIFZhbW9zIGEgdmVybG8gY29uIGVqZW1wbG9zIGNvbmNyZXRvcy4gRGUgbW9tZW50bywgcGFyYSBleHBsaWNhciBsYXMgcHJpbmNpcGFsZXMgY2FyYWN0ZXLDrXN0aWNhcyBkZSBgZ2dwbG90MmAgdXRpbGl6YXLDqSB1biBjb25qdW50byBkZSBkYXRvcyBmYW1vc28sIHBlcm8gIG9kaWFkbyBwb3IgYWxndW5vcywgcG9yIGhhYmVyIHNpZG8gdXRpbGl6YWRvIGVuIG51bWVyb3NvcyBlamVtcGxvcyB5IGN1cnNvczogZWwgW2lyaXMgZGF0YXNldF0oaHR0cHM6Ly9lcy53aWtpcGVkaWEub3JnL3dpa2kvQ29uanVudG9fZGVfZGF0b3NfZmxvcl9pcmlzKS4KCkVsIGNvbmp1bnRvIGRlIGRhdG9zIGBpcmlzYCBjb250aWVuZSBkYXRvcyBzb2JyZSAxNTAgZmxvcmVzLCBlbiBjb25jcmV0byBzb2JyZSAxNTAgbGlyaW9zLiBgaXJpc2AgdGllbmUgNSB2YXJpYWJsZXMsIDQgZGUgZWxsYXMgbWlkZW4gbGEgbG9uZ2l0dWQgeSBlbCBhbmNobyBkZWwgcMOpdGFsbyB5IHPDqXBhbG8gZGUgbG9zIDE1MCBsaXJpb3MuIEVzdGFzIDQgcHJpbWVyYXMgdmFyaWFibGVzIHNvbiBjdWFudGl0YXRpdmFzIHkgY29udGludWFzOyBtaWVudHJhcyBxdWUgbGEgcXVpbnRhIHZhcmlhYmxlIGVzIGNhdGVnw7NyaWNhLCBpbmRpY2FuZG8gbGEgY2xhc2UgbyB2YXJpZWRhZCBkZSBsb3MgbGlyaW9zLCB5YSBxdWUgZW4gbG9zIGRhdG9zIGhheSAzIGVzcGVjaWVzIGRpc3RpbnRhcyBkZSBsaXJpb3MgKHNldG9zYSwgdmVyc2ljb2xvciB5IHZpcmdpbmljYSkuIENvbiBlc3RvcyBkYXRvcywgY29uIDMgZGUgc3VzIHZhcmlhYmxlcywgc2UgaGEgY3JlYWRvIGVsIGdyw6FmaWNvIHF1ZSB2ZXMgbcOhcyBhcnJpYmEuCgo8YnI+CgoqKlBSSU1FUiBHUsOBRklDTyoqOiBQYXJhIGNvbWVuemFyIG51ZXN0cmFzIGFuZGFuemFzIGNvbiBgZ2dwbG90MmAgaW50ZW50YXJlbW9zIHJlcGxpY2FyIGNvbiBjw7NkaWdvIFIgZWwgZ3LDoWZpY28gZGUgYXJyaWJhOyBhdW5xdWUgYWwgcHJpbmNpcGlvIHPDs2xvIHV0aWxpemFyZW1vcyAyIHZhcmlhYmxlczogaGFyZW1vcyB1biBncsOhZmljbyBkZSBwdW50b3MgZGUgbGEgbG9uZ2l0dWQgZGVsIHPDqXBhbG8gZnJlbnRlIGEgbGEgbG9uZ2l0dWQgZGVsIHDDqXRhbG8uCgpIYWNlciB1biBncsOhZmljbyBjb24gYGdncGxvdDJgIHJlcXVpZXJlIGRlIHZhcmlhcyBldGFwYXMsIAoKICAtIGxhIHByaW1lcmEgZGUgZWxsYXMgY29uc2lzdGUgZW4gdXNhciBsYSBmdW5jacOzbiBgZ2dwbG90KClgIHBhcmEgaW5pY2lhbGl6YXIgZWwgZ3LDoWZpY28uIAogIAogIC0gZW4gc2VndW5kbyBsdWdhciwgdGVuZHJlbW9zIHF1ZSBlc3BlY2lmaWNhciBxdWUgY29uanVudG8gZGUgZGF0b3MgdXNhcmVtb3MgZW4gZWwgZ3LDoWZpY28uIAogIAogIC0gZW4gdGVyY2VyIGx1Z2FyIHRlbmRyZW1vcyBxdWUgZXNwZWNpZmljYXIgcXVlIHZhcmlhYmxlcyBpcsOhbiBhc29jaWFkYXMgYSBkZXRlcm1pbmFkb3MgZWxlbWVudG9zIHZpc3VhbGVzIG8gZXN0w6l0aWNvcyBkZWwgZ3LDoWZpY28gCiAgCiAgLSBwb3Igw7psdGltbywgZW4gY3VhcnRvIGx1Z2FyLCB0ZW5kcmVtb3MgcXVlIGVzcGVjaWZpY2FyIHF1ZSB0aXBvIGRlIGdyw6FmaWNvIG8gZ2VvbWV0csOtYSB1c2FyZW1vcyBwYXJhIHZpc3VhbGl6YXIgbGFzIG9ic2VydmFjaW9uZXMuCiAgCiAgClZlw6Ftb3NsbyBtw6FzIGRldGVuaWRhbWVudGUuCgpFbiBgZ2dwbG90MmAsIHBhcmEgaGFjZXIgdW4gZ3LDoWZpY28gKipzZSBlbXBpZXphIFNJRU1QUkUgbGxhbWFuZG8gYSBsYSBmdW5jacOzbiBgZ2dwbG90KClgKiouIFNpIHRlY2xlYW1vcyBgZ2dwbG90KClgIGVuIGxhIGNvbnNvbGEgbyBlbiB1biBzY3JpcHQsIHBhcmVjZSBxdWUgbm8gb2N1cnJlIG5hZGEsIHBlcm8gdHJhcyBsYSBsbGFtYWRhIGEgbGEgZnVuY2nDs24gYGdncGxvdCgpYCwgUiBoYSBjcmVhZG8gdW4gb2JqZXRvLCB1biBjb250ZW5lZG9yIHBhcmEgbnVlc3RybyBmdXR1cm8gZ3LDoWZpY28uIEHDum4gbm8gdmVtb3MgZWwgZ3LDoWZpY28sIGZhbHRhbiBjb3NhcywgcGVybyB5YSBsbyBoZW1vcyBpbmljaWFsaXphZG8uIFNpIHF1aWVyZXMgdmVyIGVsIG9iamV0by9jb250ZW5lZG9yIHF1ZSBoZW1vcyBjcmVhZG8gY29uIGxhIGxsYW1hZGEgYSBgZ2dwbG90KClgIHRpZW5lcyBxdWUgYXNpZ25hcmxlIHVuIG5vbWJyZSwgYXPDrSBwb2Ryw6FzIHZlcmxvIGVuIGxhIHBlc3Rhw7FhICJFbnZpcm9ubWVudCIgZGUgUlN0dWRpby4gCgoKUGFyYSB2ZXJsbyB0aWVuZXMgcXVlIGhhY2VyOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbXlfZ3JhZmljbyA8LSBnZ3Bsb3QoKQpgYGAKCmBteV9ncmFmaWNvYCwgZXMgdW4gb2JqZXRvIFIsIGNvbmNyZXRhbWVudGUgdW5hIGxpc3RhIGNvbiA5IGVsZW1lbnRvcywgcXVlIHRlbmRyZW1vcyBxdWUgaXIgImxsZW5hbmRvIiBwYXJhIGhhY2VyIG51ZXN0cm8gZ3LDoWZpY28uIAoKCkdlbmVyYWxtZW50ZSwgZGVudHJvIGRlIGxhIGZ1bmNpw7NuIGBnZ3Bsb3QoKWAgc2Ugc3VlbGUgZXNwZWNpZmljYXIgZWwgY29uanVudG8gZGUgZGF0b3MgcXVlIHZhcyBhIHV0aWxpemFyIHBhcmEgaGFjZXIgZWwgZ3LDoWZpY28uIEEgZGlmZXJlbmNpYSBkZSBsb3MgZ3LDoWZpY29zIGRlIFItYmFzZSwgYGdncGxvdDJgIG5vIHBlcm1pdGUgZ3JhZmljYXIgdmVjdG9yZXM6ICoqbG9zIGRhdG9zIHF1ZSBzZSBzdW1pbmlzdHJhbiBoYW4gZGUgc2VyIFNJRU1QUkUgZGF0YS5mcmFtZXMqKiBvIHNpbWlsYXJlcy4KCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoZGF0YSA9IGlyaXMpCmdncGxvdChpcmlzKQpgYGAKCgoKWWEgZXN0w6EsIGNvbiBjdWFscXVpZXJhIGRlIGxhcyAyIGluc3RydWNjaW9uZXMgZGUgYXJyaWJhLCBzb24gZXF1aXZhbGVudGVzXltUaWVuZXMgcXVlIGVudGVuZGVyIHBvcnF1ZSBzb24gZXF1aXZhbGVudGVzLiBCdXNjYSBsYSBheXVkYSBkZSBsYSBmdW5jacOzbiB5IHZlcsOhcyBxdWUgZWwgcHJpbWVyIGFyZ3VtZW50byBkZSBsYSBmdW5jacOzbiBlcyBkYXRhLCBhc8OtIHF1ZSBzaSwgY29tbyBvY3VycmUgZW4gbGEgc2VndW5kYSBleHByZXNpw7NuLCBubyBwb25lbW9zIGVsIG5vbWJyZSBkZWwgYXJndW1lbnRvIGRlIGxhIGZ1bmNpw7NuLCBSIHRvbWFyw6EgaXJpcyBjb21vIGVsIHZhbG9yIGRlbCBwcmltZXIgYXJndW1lbnRvLiBQYXJlY2UgdW4gdHJhYmFsZW5ndWFzIHBlcm8gZXMgaW1wb3J0YW50ZV0sIHlhIGhlbW9zIGluaWNpYWxpemFkbyBlbCBncsOhZmljbyB5IGxlIGhlbW9zIGRpY2hvIHF1ZSBkYXRvcyB2YW1vcyBhIHVzYXIuIAoKRGlqaW1vcyBxdWUgcGFyYSBjb21lbnphciBoYXLDrWFtb3MgdW4gZ3LDoWZpY28gZGUgcHVudG9zIGRlIGxhIHZhcmlhYmxlIGBTZXBhbC5MZW5ndGhgIGZyZW50ZSBhIGBQZXRhbC5MZW5ndGhgOyBhc8OtIHF1ZSAqKnRlbmVtb3MgcXVlIGRlY2lybGUgYSBgZ2dwbG90MmAgcXVlIHZhcmlhYmxlcyBkZSBgaXJpc2AgcXVlcmVtb3MgdmlzdWFsaXphciB5IGNvbiBxdWUgcHJvcGllZGFkZXMgZXN0w6l0aWNhcyBxdWVyZW1vcyBhc29jaWFyIGNhZGEgdmFyaWFibGUqKi4gUGFyYSBlbGxvIHV0aWxpemFyZW1vcyBsYSBmdW5jacOzbiBgYWVzKClgIGRlbnRybyBkZSBgZ2dwbG90KClgLiBMbyBoYWNlbW9zIGNvbiBsYSBzaWd1aWVudGUgZXhwcmVzacOzbjoKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTZXBhbC5MZW5ndGgsICB5ID0gUGV0YWwuTGVuZ3RoKSkKYGBgCgpBw7puIG5vIHZlbW9zIGVsIGdyw6FmaWNvLCBwZXJvIGNvbiBgYWVzKClgIGxlIGhlbW9zIGRpY2hvIGEgUi9nZ3Bsb3QyIHF1ZSBxdWVyZW1vcyBhc29jaWFyL21hcGVhciBsYSB2YXJpYWJsZSBTZXBhbC5MZW5ndGggYWwgZWplIHgsIHkgbGEgdmFyaWFibGUgUGV0YWwuTGVuZ3RoIGFsIGVqZSB5LiBGw61qYXRlIHF1ZSwgZW4gbGEgcGVzdGHDsWEgZGUgZ3LDoWZpY29zIGRlIFJTdHVkaW8sIHlhIHZlbW9zIGxvcyBlamVzIGRlbCBncsOhZmljbzsgYWRlbcOhcywgZsOtamF0ZSBxdWUgYGdncGxvdDJgIHlhIGhhIGNhbGN1bGFkbyBwYXJhIG5vc290cm9zIGVsIHJhbmdvIGRlIGxhcyB2YXJpYWJsZXMgcGFyYSBhanVzdGFyIGxvcyBlamVzLgoKCkFsIGlndWFsIHF1ZSBhbnRlcywgcG9kZW1vcyBvbWl0aXIgbG9zIG5vbWJyZXMgZGUgbGFzIG9wY2lvbmVzIGRlIGxhIGZ1bmNpw7NuIGBhZXMoKWAuIEVzdGEgZnVuY2nDs24gcHVlZGUgdGVuZXIgbXVjaG9zIGFyZ3VtZW50b3MgKHZlcmVtb3MgYWxndW5vcyksIHBlcm8gbG9zIDIgcHJpbWVyb3Mgc29uIHNpZW1wcmUgeCAoZWwgZWplIHgpIHkgZGVzcHXDqXMgeSAoZWwgZWplIHkgbyB2ZXJ0aWNhbCBkZSBtaSBncsOhZmljbyk7IGVzIGRlY2lyIHBvZHLDrWFtb3MgaGFjZXIgbG8gc2lndWllbnRlIHF1ZSBlcyBtw6FzIHLDoXBpZG8gZGUgdGVjbGVhci4KCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkKYGBgCgpDb24gZXN0YSBpbnN0cnVjY2nDs24gbGUgZXN0YW1vcyBkaWNpZW5kbyBhIGBnZ3Bsb3QyYCBxdWUgdmFtb3MgYSBoYWNlciB1biBncsOhZmljbyBjb24gbG9zIGRhdG9zIGRlbCBkYXRhLmZyYW1lIGBpcmlzYCB5IHF1ZSB2YW1vcyBhIGFzb2NpYXIvY29uZWN0YXIvbWFwZWFyIGxhIHZhcmlhYmxlIGBTZXBhbC5MZW5ndGhgIGNvbiBlbCBlamUgeCwgeSBsYSB2YXJpYWJsZSBgUGV0YWwuTGVuZ3RoYCBjb24gZWwgZWplIHkgZGVsIGdyw6FmaWNvLiBQZXJmZWN0bywgcGVybyBlbnRvbmNlcyDCv3BvciBxdcOpIG5vIHZlbW9zIGVsIGdyw6FmaWNvPyBMYSByYXrDs24gZXN0cmliYSBlbiBxdWUgbm8gbGUgaGVtb3MgZGljaG8gYSBgZ2dwbG90MmAgcXXDqSB0aXBvIGRlIGdyw6FmaWNvIHF1ZXJlbW9zIChkZSBwdW50b3MsIGRlIGxpbmVhcyBldGMuLi4pLiBFbCB0aXBvIGRlIGdyw6FmaWNvIHNlIGV4cGxpY2l0YSBjb24gdW5hIGZhbWlsaWEgZGUgZnVuY2lvbmVzOiBgZ2VvbV94eCgpYCBvICoqZ2VvbSoqZXRyw61hcy4gSGF5IG11Y2hhcyBnZW9tZXRyw61hcyBvIHRpcG9zIGRlIGdyw6FmaWNvcyBxdWUgcG9kZW1vcyB1c2FyLiBZYSBsbyB2ZXJlbW9zISEhIE5vc290cm9zIHF1ZXJlbW9zIGhhY2VyIHVuIGdyw6FmaWNvIGRlIHB1bnRvcywgYXPDrSBxdWUsIGRlIGxhIGZhbWlsaWEgZGUgKipnZW9tKipldHLDrWFzIHRlbmVtb3MgcXVlICB1c2FyIGBnZW9tX3BvaW50KClgLiBWZcOhbW9zbG8uCgoKYGBge3IsIG91dC53aWR0aCA9ICI1MCUifQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkKYGBgCgpDb21vIHZlbW9zIGxvcyBwdW50b3MgZGVsIGdyw6FmaWNvIHNlIHZpc3VhbGl6YW4gZW4gY29sb3IgbmVncm8sIGNvbiB1biB0YW1hw7FvLCB1bmEgdHJhbnNwYXJlbmNpYSB5IHVuYSBmb3JtYSBkZXRlcm1pbmFkYXMuIE9idmlhbWVudGUgdG9kbyBlc3RvIHNlIHB1ZWRlIGNhbWJpYXIgZGVudHJvIGRlIGBnZW9tX3BvaW50KClgIGNvbiBsYXMgb3BjaW9uZXMgYWRlY3VhZGFzLiBQb3IgZWplbXBsbzoKCgpgYGB7ciwgb3V0LndpZHRoID0gIjUwJSJ9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoY29sb3IgPSAicmVkIiwgc2l6ZSA9IDIsIGFscGhhID0gMC4yKQpgYGAKCgoKWWEgY2FzaSBlc3TDoS4gTm8gZXMgdGFuIGNvbXBsaWNhZG8uIFlhIGhlbW9zIHZpc3RvIGxhcyBpZGVhcyBmdW5kYW1lbnRhbGVzIGRlIGxhIHZpc3VhbGl6YWNpw7NuIGNvbiBgZ2dwbG90MmA6CgoxLiBMb3MgZ3LDoWZpY29zIHNlIGluaWNpYW4gY29uIGxhIGZ1bmNpw7NuIGBnZ3Bsb3QoKWAuIEdlbmVyYWxtZW50ZSBhcXXDrSBzZSBlc3BlY2lmaWNhbiBsb3MgZGF0b3MgKGRhdGEuZnJhbWUpIHF1ZSBxdWVyZW1vcyB1dGlsaXphciAgCgoyLiBMYSBmdW5jacOzbiBgYWVzKClgIHNpcnZlIHBhcmEgYXNvY2lhci9tYXBwZWFyIHZhcmlhYmxlcyBjb24gYXRyaWJ1dG9zL2NhcmFjdGVyw61zdGljYXMgZXN0w6l0aWNhcyBkZWwgZ3LDoWZpY28uIERlIGhlY2hvIGVsIG5vbWJyZSBkZWwgZnVuY2nDs24gYGFlcygpYCB2aWVuZSBkZSAqKmFlcyoqdGhldGljcy4gTGFzICoqYWVzKip0aGV0aWNzIG3DoXMgaW1wb3J0YW50ZXMgZGUgdW4gZ3LDoWZpY28gc3VlbGVuIHNlciBsb3MgZWplcyB4IGUgeSwgcG9yIGVzbyBzZSBwb25lbiBzaWVtcHJlIGFsIHByaW5jaXBpbyBkZSBgYWVzKClgOyBlcyBkZWNpciwgc29uIGxvcyAyIHByaW1lcm9zIGFyZ3VtZW50b3MgZGUgYGFlcygpYC4gRW4gbnVlc3RybyBlamVtcGxvIGhlbW9zIGFzb2NpYWRvIGxhIHZhcmlhYmxlIGBTZXBhbC5MZW5ndGhgIGNvbiBlbCBlamUgeCwgeSBsYSB2YXJpYWJsZSBgUGV0YWwuTGVuZ3RoYCBjb24gZWwgZWplIHkuIFZlcmVtb3MgbcOhcyAqKmFlcyoqdGhldGljcywgY29tbyBwb3IgZWplbXBsbyBlbCBjb2xvciBvIGVsIHRhbWHDsW8gKGRlIGxvcyBwdW50b3MgLi4uIG8gZGUgbGFzIGxpbmVhcyBvIC4uLikKCjMuIENvbiBgZ2VvbV8qKigpYCBlbGVnaW1vcyBlbCB0aXBvIG8gKipnZW9tKipldHLDrWEgZGUgZ3LDoWZpY28uIEhheSBtdWNob3MgdGlwb3MgZGUgZ3LDoWZpY29zLCBhc8OtIHF1ZSBoYWJyw6FuIG11Y2hvcyAqKmdlb21zKiouIFBvciBlamVtcGxvOiBnZW9tX3BvaW50KCksIGdlb21fbGluZSgpLCBnZW9tXyAuLi4KCgpFc3RhcyAzIGlkZWFzIHNvbiBsYXMgcHJpbmNpcGFsZXMgcGFyYSBlbnRlbmRlciBnZ3Bsb3QyLiBEZXNwdcOpcyBoYXkgbXVjaGFzIG3DoXMgb3BjaW9uZXMgeSBlbGVtZW50b3MgcXVlIHNlcsOhbiBtdXkgaW1wb3J0YW50ZXMgcGFyYSBjb25zZWd1aXIgdW4gYnVlbiBncsOhZmljbywgcGVybyBlbiBjaWVydGEgZm9ybWEgc29uIHNlY3VuZGFyaWFzOyBwb3IgZWplbXBsbywgbG9zIHTDrXR1bG9zLCBsb3MgZWplcywgbGFzIGVzY2FsYXMsIGVsIHRlbWEgZXRjLi4uIGxvIGlyZW1vcyB2aWVuZG8gcG9jbyBhIHBvY28uCgo8YnI+CgpBZmlhbmNlbW9zIGxhcyBpZGVhcyBwcmluY2lwYWxlcyBkZSBsYSB2aXN1YWxpemFjacOzbiBjb24gYGdncGxvdDJgLiDCv1BpZW5zYSBxdWUgaGFyw6EgbGEgc2lndWllbnRlIGxpbmVhIGRlIGPDs2RpZ28/CgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9saW5lKCkKYGBgCgpMYSBwcmltZXJhIHBhcnRlIGRlIGxhIGluc3RydWNjacOzbiBlcyBpZ3VhbCBhIGxhIGFudGVyaW9yOiBxdWVyZW1vcyB1biBncsOhZmljbyBjb24gZWwgZGF0YS5mcmFtZSBpcmlzIHkgcXVlcmVtb3MgYXNvY2lhciBsYSB2YXJpYWJsZSBTZXBhbC5MZW5ndGggY29uIGVsIGVqZSB4LCBjb24gbGEgcHJvcGllZGFkICoqYWVzKip0aGV0aWMgZWplIHggeSBQZXRhbC5MZW5ndGggY29uIGVsIGVqZSB5OyBwZXJvIGxlIGhlbW9zIHBlZGlkbyB1biBncsOhZmljbyBkZSBsaW5lYXMgKGdlb21fbGluZSgpKS4gRW4gZXN0ZSBjYXNvIGhhY2VyIHVuIGdyw6FmaWNvIGRlIGxpbmVhcyBubyB0aWVuZSBtdWNobyBzZW50aWRvLCBwZXJvIHNpIHNlIGxvIHBlZGltb3MgYSBSLCBlc3RlIG5vcyBoYWNlIGNhc28geSBub3MgbG8gbXVlc3RyYS4KCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiNDklIn0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9saW5lKCkKYGBgCgo8YnI+CgpZYSBkaWplIHF1ZSB1bmEgZGUgbGFzIGNhcmFjdGVyw61zdGljYXMgaW1wb3J0YW50ZXMgZGUgYGdncGxvdDJgICBlcyBxdWUgZnVuY2lvbmEgcG9yIGNhcGFzIHF1ZSBzZSB2YW4gc3VwZXJwb25pZW5kbzsgcGFyYSBpciBhw7FhZGllbmRvIGNhcGFzIGEgbnVlc3RybyBncsOhZmljbyB0ZW5lbW9zIHF1ZSB1c2FyIGVsIHPDrW1ib2xvICoqYCtgKiouIFBvciBlamVtcGxvIHNpIHF1aXNpw6lyYW1vcyB2ZXIgbG9zIHB1bnRvcyB5IGxhcyBsaW5lYXMgwr9Dw7NtbyBsbyBoYWNlbW9zPwoKCmBgYHtyLCBvdXQud2lkdGggPSAiNDklIn0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9saW5lKCkKYGBgCgo8YnI+CgpUYW1wb2NvIHNvbiBtdXkgw7p0aWxlcyBsYSBsaW5lYXMgZW4gZXN0ZSBncsOhZmljby4KCgoKT3RyYSBnZW9tZXRyw61hIG8gKipnZW9tXygpKiogcXVlIHNlIHVzYSBtdWNobyBlcyBgZ2VvbV9zbW9vdGgoKWAuIFByb2LDqW1vc2xhOgoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkgKyAgZ2VvbV9zbW9vdGgoKQpgYGAKCkRlbCBncsOhZmljbyBwdWVkZSBpbmZlcmlyc2UgcXVlIGhheSB1bmEgcmVsYWNpw7NuLCBubyBsaW5lYWwsIHBlcm8gc8OtIGRpcmVjdGEgbyBwb3NpdGl2YSBlbnRyZSBsYSBsb25naXR1ZCBkZWwgc8OpcGFsbyB5IGRlbCBww6l0YWxvOyBwZXJvIHRhbWJpw6luIHNlIGFwcmVjaWEgcXVlIGhheSBhbCBtZW5vcyBkb3MgZ3J1cG9zIGRpc3RpbnRvcyBkZSBsaXJpb3MuIEhheSB1biBncnVwbyBkZSBvYnNlcnZhY2lvbmVzIGN1eW8gcMOpdGFsbyBwYXJlY2Ugc2VyIGNsYXJhbWVudGUgbWVub3IgcXVlIGVsIGRlbCByZXN0byBkZSBsaXJpb3MuIFZlYW1vcyBzaSBlc3RvIHNlIGRlYmUgbyBlc3RhIGFzb2NpYWRvIGFsIHRpcG8gZGUgbGlyaW8sIHJlY3VlcmRhIHF1ZSBoYXkgMyB0aXBvcyBkZSBsaXJpb3MsIGFzb2NpYWRvcyBhIGxhIHZhcmlhYmxlIGBpcmlzJFNwZWNpZXNgLiBQYXJhIHZlcmxvIGVuIG51ZXN0cm8gZ3LDoWZpY28gbG8gcXVlIHZhbW9zIGEgaGFjZXIgZXMgYXNvY2lhci9tYXBlYXIgbGEgdmFyaWFibGUgYFNwZWNpZXNgIGNvbiBsYSAqKmFlcyoqdGhldGljcyBjb2xvcjoKCgoKYGBge3J9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykpICsgZ2VvbV9wb2ludCgpIApgYGAKClB1ZXMgcGFyZWNlIHF1ZSBzw60sIHF1ZSBsYSBlc3BlY2llIGRlIGxpcmlvcyAic2V0b3NhIiBlcyBtw6FzIHBlcXVlw7FhLCBhbCBtZW5vcyBlbiBsb25naXR1ZCwgZGVsIHPDqXBhbG8sIHBlcm8gc29icmUgdG9kbyBkZWwgcMOpdGFsby4KCkxhIHZhcmlhYmxlIGBTcGVjaWVzYCBwb2Ryw61hbW9zIGhhYmVybGEgYXNvY2lhZG8gYSBsYSBlc3TDqXRpY2EgdGFtYcOxbyAoc2l6ZSksIG8gYSBsYSBlc3TDqXRpY2EgZm9ybWEgKHNoYXBlKSBwZXJvIG5vIHNlcsOtYSB0YW4gw7p0aWwgbmkgcXVlZGFyw61hIHRhbiBib25pdG8gZWwgZ3LDoWZpY28uIEbDrWphdGUgcXVlIGluY2x1c28gUiBub3MgYXZpc2EgZGUgcXVlIGFzb2NpYXIgbGEgcHJvcGllZGFkIG8gKiphZXMqKnRoZXRpYyB0YW1hw7FvIGNvbiB1bmEgdmFyaWFibGUgY2F0ZWfDs3JpY2EgY29tbyBTcGVjaWVzIG5vIGVzIG11eSByZWNvbWVuZGFibGUuCgoKYGBge3IsIHdhcm5pbmcgPSBUUlVFfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBzaXplID0gU3BlY2llcykpICsgZ2VvbV9wb2ludCgpIApgYGAKClRhbWJpw6luIHBvZGVtb3MgYXNvY2lhciBsYSB2YXJpYWJsZSBgU3BlY2llc2AgYSBsYSBlc3TDqXRpY2EgImZvcm1hIihzaGFwZSkuIExvIGhhY2Vtb3MgZW4gbGEgZXhwcmVzacOzbiBkZSBtw6FzIGFiYWpvLiBDb21vIHZlaXMsIGFob3JhIGxhcyBkaWZlcmVuY2lhcyBlbnRyZSBlc3BlY2llcyBkZSBsaXJpb3Mgbm8gc2UgYXByZWNpYW4gdGFuIGJpZW4gY29tbyBjdWFuZG8gdXPDoWJhbW9zIGBjb2xvciA9IFNwZWNpZXNgCgoKYGBge3J9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIHNoYXBlID0gU3BlY2llcykpICsgZ2VvbV9wb2ludCgpIApgYGAKCgpPcyB2YSBhIGNvc3RhciBoYWNlciBncsOhZmljb3MsIG5vcm1hbCEhISwgcGVybyBlc3Blcm8gcXVlIGxhIGlkZWEgcHJpbmNpcGFsIHlhIGxhIHRlbmfDoWlzLiBMbyBxdWUgcGFzYSBlcyBxdWUgbm8gb3MgaGUgY29udGFkbyB0b2RvLCBlbiByZWFsaWRhZCBlcyB1biBwb2NvIG3DoXMgY29tcGxlam8geSB2ZXJzw6F0aWwuIExvIG1lZGlvIGV4cGxpY28gZW4gZWwgc2lndWllbnRlIGFwYXJ0YWRvLgoKCjxicj4KCiMjIyAgTcOhcyBpZGVhcyBzb2JyZSBnZ3Bsb3QyCgpZYSB0ZW7DqWlzIGxhcyBpZGVhcyBwcmluY2lwYWxlcyBwYXJhIGhhY2VyIGdyw6FmaWNvcyBjb24gYGdncGxvdDJgLCBwZXJvIG5vIG9zIGxvIGhlIGNvbnRhZG8gdG9kbywgdGFtcG9jbyBsbyB2b3kgYSBoYWNlciBhaG9yYSwgcGVybyBzaSBjb250YXLDqSBsYXMgY29zYXMgZGUgdW5hIGZvcm1hIGRpZmVyZW50ZSBwYXJhIHF1ZSB0ZW5nw6FpcyBtw6FzIGZsZXhpYmlsaWRhZC92ZXJzYXRpbGlkYWQgYSBsYSBob3JhIGRlIGhhY2VyIGdyw6FmaWNvcy4gU2kgcXVlcsOpaXMgc2FiZXIgdG9kYSBsYSB2ZXJkYWReW1NpIGFsZ3VubyBkZSB2b3NvdHJvcyBtZSBwcmVndW50YSBwb3IgbGEgdmVyZGFkIGFkb3B0YXLDqSBzZW1ibGFudGUgaGllcsOhdGljbyB5IHPDs2xvIGRpcsOpIGxvIHNpZ3VpZW50ZTogZ2VvbV9wb2ludCgpIGlzIGEgc2hvcnQtaGFuZCBmb3IgbGF5ZXIoZ2VvbSA9ICJwb2ludCIsIHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpIMOzIGFwZWxhcsOpIGEgbGEgY2FuY2nDs24gZGUgQWxiZXJ0IFBsYSwgc2Vnw7puIG1lIGRlLl0gdGVuZHLDqWlzIHF1ZSBpciBhbCBsaWJybyBkZSBIYWRsZXksIGNvbmNyZXRhbWVudGUgW2FxdcOtXShodHRwczovL2dncGxvdDItYm9vay5vcmcvbGF5ZXJzLmh0bWwpLgoKTGEgZm9ybWEgcXVlIG9zIGhlIGNvbnRhZG8gIGRlIGhhY2VyIGdyw6FmaWNvcyBgZ2dwbG90MmAgZXMgbGEgcXVlIHZlcsOpaXMgaGFiaXR1YWxtZW50ZSwgeW8gdGFtYmnDqW4gaGFnbyBtaXMgZ3LDoWZpY29zIGFzw60sIHNvbG8gcXVlIHBhcmEgZW50ZW5kZXIgbWVqb3IgZWwgZnVuY2lvbmFtaWVudG8sIGxhIHNpbnRheGlzIGRlIGBnZ3Bsb3QyYCwgb3MgbG8gdGVuZ28gcXVlIGNvbnRhciBvdHJhIHZleiBkZSB1bmEgZm9ybWEgdW4gcG9jbyBkaWZlcmVudGUgbyBhbXBsaWFkYS4gCgpVbiBncsOhZmljbyBkZSBnZ3Bsb3QyIHNlIGluaWNpYSBsbGFtYW5kbyBhIGxhIGZ1bmNpw7NuIGBnZ3Bsb3QoKWAgZXNvIGVzIGNpZXJ0byB5IHRhbWJpw6luIGVzIHZlcmRhZCBxdWUgZ2VuZXJhbG1lbnRlIGRlbnRybyBkZSBgZ2dwbG90KClgIHNlIGluZGljYSBlbCBkYXRhLmZyYW1lIHF1ZSB2YXMgYSB1dGlsaXphciB5IGNvbiBgYWVzKClgIHF1ZSB2YXJpYWJsZXMgdmFzIGEgdXNhciB5IGNvbiBxdWUgZWxlbWVudG9zIHZpc3VhbGVzIG8gZXN0w6l0aWNvcyBxdWllcmVzIGFzb2NpYXIgY2FkYSB1bmEgZGUgbGEgdmFyaWFibGVzIHF1ZSB2YXMgYSB1dGlsaXphcl5bRW4gYGFlcygpYCBubyBzw7NsbyBwdWVkZXMgcG9uZXIgdmFyaWFibGVzLCBzaW5vIHRyYW5zZm9ybWFjaW9uZXMgZGUgZWxsYXMsIHBvciBlamVtcGxvIGFlcyh4ID0gdjEgXiAyLCB5ID0gdjEgLyB2MikuIFRhbWJpw6luIHNlIHB1ZWRlbiBtYXBwZWFyIHZhcmlhYmxlcyBjb24gY29uc3RhbnRlcyBhZXMoeCA9IDEsIGNvbG91ciA9ICJsb3F1ZXNlYSIpXS4gQ29ycmVjdG8sIHBlcm8gLi4uLiBlbiByZWFsaWRhZCB1biBncsOhZmljbyBnZ3Bsb3QyIHNlIGhhY2UgcG9yIGNhcGFzLCBjYWRhIGNhcGEgc2UgZXNwZWNpZmljYSBjb24gdW5hIGZ1bmNpw7NuIGRlIGxhIGZhbWlsaWEgYGdlb21feHgoKWAsIGFzw60gcXVlIGVuIHJlYWxpZGFkIGxvcyBkYXRvcyB5IGxhcyBgYWVzKClgIHNlICJkZWJlcsOtYW4iIGVzcGVjaWZpY2FyIGRlbnRybyBkZSBsYSBmdW5jacOzbiBnZW9tX3h4KClgLiAKClBhcmVjZSB1biBwb2NvIGRlIGzDrW8gcGVybyBlbiBjdWFudG8gbG8gZW50aWVuZGFzIGVzIG11eSBmw6FjaWwgeSB0ZSBwdWVkZSBkYXIgbcOhcyBmbGV4aWJpbGlkYWQgYSBsYSBob3JhIGRlIGhhY2VyIHR1cyBncsOhZmljb3MuIEVtcGVjZW1vczogwr9yZWN1ZXJkYXMgcXVlIGhhY2VuIGxhcyBsaW5lYXMvZXhwcmVzaW9uZXMgZGUgYWJham8/CgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9wb2ludCgpIApnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX2xpbmUoKQpgYGAKClBvZGVtb3MgcGVuc2FyIHF1ZSBsYXMgZnVuY2lvbmVzIHF1ZSBoYWNlbiBsYSByZXByZXNlbnRhY2nDs24gZ3LDoWZpY2EgcmVhbG1lbnRlIHNvbiBsYXMgYGdlb21feHgoKWA7IGVzIGFow60gZG9uZGUgZGViZXLDrWFtb3MgZXNwZWNpZmljYXIgbG9zIGRhdG9zIHkgdmFyaWFibGVzL2VzdMOpdGljYXMgcXVlIHF1ZXJlbW9zIHVzYXIsIHBlcm8gc2kgbm8gbGFzIGVzcGVjaWZpY2Ftb3MgZW4gbGEgZnVuY2nDs24gYGdlb20oKWAsIGVudG9uY2VzLCBnZ3Bsb3QyIG1pcmFyw6EgYSB2ZXIgc2kgZXhpc3Rlbiwgc2kgZXN0w6FuIGVzcGVjaWZpY2Fkb3MsIGRlbnRybyBkZSBgZ2dwbG90KClgLgoKCkFob3JhIHF1ZSB5YSBzYWJlbW9zIGVsIGZ1bmNpb25hbWllbnRvIGLDoXNpY28gZGUgYGdncGxvdDJgLCB2ZWFtb3MgYWxndW5vcyBkZXRhbGxlcyBtZWRpYW50ZSBhbGd1bm9zIGVqZW1wbG9zLiBIYXN0YSBhaG9yYSBoZW1vcyBlc3BlY2lmaWNhZG8gZWwgZGF0YS5mcmFtZSBxdWUgcXVlcmVtb3MgZ3JhZmljYXIgY29uIGBnZ3Bsb3QoZGF0YSA9IG15X2RmKWAgbyBjb24gYGdncGxvdChteV9kZilgIHkgbGFzIHZhcmlhYmxlcyBxdWUgcXVlcmVtb3MgdmVyLCB5IGEgcXVlIHByb3BpZWRhZCBlc3TDqXRpY2EgcXVlcmVtb3MgYXNvY2lhcmxhLCBjb24gbGEgZnVuY2nDs24gYGFlcygpYCAqKmRlbnRybyBkZSBgZ2dwbG90KClgKiouIFNpIGxvIGhhY2Vtb3MgYXPDrSwgdG9kb3MgbG9zIGBnZW9tc194eCgpYCBxdWUgdXRpbGljZW1vcyBjb21wYXJ0aXLDoW4gZWwgY29uanVudG8gZGUgZGF0b3MgeSBsYXMgdmFyaWFibGVzL2VzdMOpdGljYXMgYSBtYXBwZWFyIHkgbW9zdHJhcjsgcGVybyBhIHZlY2VzLCBlbiBncsOhZmljb3MgbcOhcyBjb21wbGVqb3MgcG9kZW1vcyBxdWVyZXIgaGFjZXIgcXVlIGNhZGEgYGdlb21feHgoKWAgbXVlc3RyZSBkYXRvcyB5L28gdmFyaWFibGVzIGRpc3RpbnRhcy4gCgpFbnRlbmRlciBxdWUgY2FkYSBgZ2VvbV94eCgpYCBwdWVkZSBlc3RhciBhc29jaWFkbyBhIGRpc3RpbnRvcyBkYXRhLmZyYW1lcyB5L28gdmFyaWFibGVzIGVzIGltcG9ydGFudGUgcGFyYSB0ZW5lciBtw6FzIHZlcnNhdGlsaWRhZCBjb24gYGdncGxvdDJgLlBvciBlamVtcGxvLCBsYXMgc2lndWllbnRlcyB0cmVzIGV4cHJlc2lvbmVzIGhhY2VuIGVsIG1pc21vIGdyw6FmaWNvLiBTZSBzdWVsZSB1dGlsaXphciBsYSBwcmltZXJhIGV4cHJlc2nDs24sIHBlcm8gbGEgc2VndW5kYSB5IHRlcmNlcmEgZXhwcmVzaW9uZXMgc29uICJtw6FzIGZsZXhpYmxlcyIsIGF1bnF1ZSBlcyB2ZXJkYWQgcXVlIHNpIHPDs2xvIHNlIHV0aWxpemEgdW4gYGdlb21feHgoKWAgbm8gZ2FuYW1vcyBuYWRhIHBvciB1c2FyIGxhIHNlZ3VuZGEgbyB0ZXJjZXJhIGV4cHJlc2nDs24sIHBlcm8gbm8gc2Vyw6EgZWwgY2FzbyBzaSBlbiBudWVzdHJvIGdyw6FmaWNvIG5lY2VzaXRhbW9zIHVzYXIgdmFyaW9zIGBnZW9tX3h4KClgCgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKQoKZ2dwbG90KGlyaXMpICsgZ2VvbV9wb2ludChhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKQoKZ2dwbG90KCkgKyBnZW9tX3BvaW50KGRhdGEgPSBpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKQpgYGAKCkbDrWphdGUsIGNvbW8gZGV0YWxsZSwgcGVybyBpbXBvcnRhbnRlLCBxdWUgc2kgdXRpbGl6YXMgbGEgdGVyY2VyYSBleHByZXNpw7NuOyBlcyBkZWNpciwgc2kgZXNwZWNpZmljYXMgbG9zIGRhdG9zIGRlbnRybyBkZSBsYSBmdW5jacOzbiBgZ2VvbV94eCgpYCwgZXMgbmVjZXNhcmlvIHBvbmVyIGVsIG5vbWJyZSBkZWwgYXJndW1lbnRvIDsgZXMgZGVjaXIsIGRlYmVzIHBvbmVyIGBkYXRhID0gaXJpc2AsIG5vIHB1ZWRlcyBwb25lciBzb2xvIGBpcmlzYC4gWW8gbWUgb2x2aWRvIHNpZW1wcmUgZGUgZXN0ZSBkZXRhbGxlXltFbiBlc3RlIGNhc28gc8OtIHF1aWVybyBxdWUgbWUgcHJlZ3VudMOpaXMgcG9yIGVzdGUgZGV0YWxsZS4gRXMgYWxnbyBxdWUgeWEgZGViZXLDrWFzIHNhYmVyL2ludHVpciBwZXJvIC4uLl0uCgpgYGB7ciwgZWNobyA9IEZBTFNFfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkKYGBgCgoKCkVuIGVzdGUgY2FzbyAoY29tbyBlbCBncsOhZmljbyBzb2xvIHRpZW5lIHVuYSBjYXBhLCBjb21vIHPDs2xvIHVzYW1vcyB1biBgZ2VvbShfeHgpYCkgbm8gZ2FuYW1vcyBuYWRhIHBvciB1c2FyIGxhIHNlZ3VuZGEgbyB0ZXJjZXJhIGV4cHJlc2nDs247IFBFUk8sIGN1YW5kbyB1c2Vtb3MgdmFyaW9zIGBnZW9tX3h4KClgIGVzdG8gbm9zIGRhcsOhIG11Y2hhcyBwb3NpYmlsaWRhZGVzIHBhcmEgbnVlc3RybyBncsOhZmljby4gCgpJbnRlbnRhIGRlc2N1YnJpciBsYXMgZGlmZXJlbmNpYXMgeSBmdW5jaW9uYW1pZW50byBkZSBsYXMgMyBzaWd1aWVudGVzIGluc3RydWNjaW9uZXMuIFJlY3VlcmRhIHF1ZSBwdWVkZXMgY29ycmVyIGxhcyBpbnN0cnVjY2lvbmVzIGVuIFIgcGFyYSB2ZXIgcXVlIGhhY2VuIGV4YWN0YW1lbnRlLgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpCmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gU3BlY2llcykpICsgZ2VvbV9zbW9vdGgoKQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBTcGVjaWVzKSkKYGBgCgoKVmXDoW1vc2xhcyB1bmEgYSB1bmE6CgoxLiBMb3MgZGF0b3MgeSBsYXMgMyB2YXJpYWJsZXMvZXN0w6l0aWNhcyBkZW50cm8gZGUgZ2dwbG90KCkKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBjb2xvciA9IFNwZWNpZXMpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkKYGBgCgpFbiBlc3RlIGNhc28gbG9zIDIgZ2VvbXMgY29tcGFydGVuIGVsIGNvbmp1bnRvIGRlIGRhdG9zIChpcmlzKSB5IGxhcyB2YXJpYWJsZXMvZXN0w6l0aWNhcyBhIGdyYWZpY2FyCgo8YnI+CgoyLiBMb3MgZGF0b3MgeSAyIHZhcmlhYmxlcy9lc3TDqXRpY2FzIGRlbnRybyBkZSBnZ3Bsb3QoKSwgcGVybyB1bmEgdGVyY2VyYSB2YXJpYWJsZSAoU3BlY2llcyksIGFzb2NpYWRhIGEgbGEgZXN0w6l0aWNhIGNvbG9yLCBhcGFyZWNlIHNvbGFtZW50ZSBlbiBgZ2VvbV9wb2ludCgpYAoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IFNwZWNpZXMpKSArIGdlb21fc21vb3RoKCkKYGBgCgo8YnI+CgozLiBMb3MgZGF0b3MgeSAyIHZhcmlhYmxlcy9lc3TDqXRpY2FzIGRlbnRybyBkZSBnZ3Bsb3QoKSwgcGVybyB1bmEgdGVyY2VyYSB2YXJpYWJsZSAoU3BlY2llcyksIGFzb2NpYWRhIGEgbGEgZXN0w6l0aWNhIGNvbG9yLCBhcGFyZWNlIHNvbGFtZW50ZSBlbiBgZ2VvbV9zbW9vdGgoKWAKCgoKYGBge3J9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IFNwZWNpZXMpKQpgYGAKCkVzcGVybywgc2VndXJvISEsIHF1ZSB0ZSBoYXMgZGFkbyBjdWVudGEgZGUgcXVlIHNpIGVzcGVjaWZpY2FzIGVsIGRhdGEuZnJhbWUgeSBsYXMgdmFyaWFibGVzL2VzdMOpdGljYXMgZGVudHJvIGRlIGBnZ3Bsb3QoKWAgZXN0byBhZmVjdGFyw6EgYSB0b2RvcyBsb3MgZ2VvbXMgZGVsIGdyw6FmaWNvOyBwZXJvIGxvIHF1ZSBzZSBlc3BlY2lmaXF1ZSBkZW50cm8gZGUgdW4gYGdlb21feHgoKWAgc29sbyBhZmVjdGEgYSBlc2EgZ2VvbWV0csOtYS4KCjxicj4KCioqT3RybyBlamVtcGxvKiogcGFyYSBlbnRlbmRlcmxvLCDCv3BvciBxdcOpIG5vIGZ1bmNpb25hIGxhIHNpZ3VpZW50ZSBleHByZXNpw7NuPwoKYGBge3IsIGV2YWwgPSBGQUxTRSwgbWVzc2FnZSA9IFRSVUV9CmdncGxvdChpcmlzKSArIGdlb21fcG9pbnQoYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBTcGVjaWVzKSkKYGBgCgpQdWVzIHBvcnF1ZSBwYXJhIHBvZGVyIHJlcHJlc2VudGFyIGxhIGxpbmVhIHN1YXZpemFkYSBzZSB1dGlsaXphIGBnZW9tX3Ntb290aCgpYCwgeSBgZ2VvbV9zbW9vdGgoKWAgbmVjZXNpdGEgY29tbyBtw61uaW1vIHRlbmVyIHZhcmlhYmxlcyBhc29jaWFkYXMgYSBsYXMgZXN0w6l0aWNhcyB4IGUgeS4gQ29tbyB2ZWlzLCBkZW50cm8gZGUgYGdlb21fc21vb3RoKClgIHNvbG8gaGVtb3MgZXNwZWNpZmljYWRvIGxhIGVzdMOpdGljYSAiY29sb3IiIHkgdGFtcG9jbyBoZW1vcyBlc3BlY2lmaWNhZG8gZW4gbGEgZnVuY2nDs24gYGdncGxvdCgpYCBxdWUgdmFyaWFibGVzIHNlIGFzb2NpYW4gY29uIHggZSB5LiBQb3IgbG8gdGFudG8sIGBnZW9tX3Ntb290aCgpYCBubyBwdWVkZSBoYWNlciBzdSB0cmFiYWpvLCBsZSBmYWx0YW4gbG9zICJkYXRvcyIgZGUgeCBlIHkgcGFyYSBjYWxjdWxhci9vYnRlbmVyIGxhIGxpbmVhIHN1YXZpemFkYS4gCgpWYW1vcyBjb24gKipvdHJvcyBlamVtcGxvcyoqLiBMYSBzaWd1aWVudGVzIGV4cHJlc2lvbmVzIHRhbXBvY28gZnVuY2lvbmFyw6FuXltFbiByZWFsaWRhZCBsYSB0ZXJjZXJhIGV4cHJlc2nDs24gc8OtIGNvcnJlcsOhLCBwZXJvIHPDs2xvIG1vc3RyYXLDoSBsb3MgcHVudG9zLCBubyBsYXMgbGluZWFzLl0gc2kgaW50ZW50w6FpcyBjb3JyZXJsYXMgZW4gdnVlc3RybyBvcmRlbmFkb3IuIMK/UG9yIHF1w6k/CgoKCmBgYHtyLCBldmFsID0gRkFMU0UsIG1lc3NhZ2UgPSBUUlVFfQpnZ3Bsb3QoKSArIGdlb21fcG9pbnQoZGF0YSA9IGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9saW5lKGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpCgpnZ3Bsb3QoYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KGRhdGEgPSBpcmlzKSArIGdlb21fbGluZSgpCgpnZ3Bsb3QoKSArIGdlb21fcG9pbnQoZGF0YSA9IGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9saW5lKCkKYGBgCgo8YnI+CgoqKk90cm8gZWplbXBsbzoqKiBoYWdhbW9zIGFsZ28gbcOhcyBtYXJjaWFuby9jb21wbGljYWRvLiBTdXDDs24gcXVlIHF1aWVyZXMgaGFjZXIgdW4gZ3LDoWZpY28gZGlmZXJlbmNpYW5kbyBsb3MgcHVudG9zIHBvciBjb2xvciBwYXJhIGxhcyB0cmVzIGVzcGVjaWVzIGRlIGxpcmlvcywgcGVybyBxdWllcmVzIHF1ZSBzb2xvIHNlIHZlYSBsYSBsaW5lYSBzdWF2aXphZGEgcGFyYSBsYXMgZG9zIGVzcGVjaWVzIG3DoXMgZ3JhbmRlcyAodmlyZ2luaWNhIHkgdmVyc2ljb2xvcikuIExvcyBsaXJpb3MgbcOhcyBwZXF1ZcOxb3Mgc29uIGxvcyBkZSBsYSBjbGFzZSBzZXRvc2EuIElndWFsIHNlIHB1ZWRlIGhhY2VyIGRlIG90cmEgZm9ybWEgcGVybyBsYSBxdWUgbWUgdmllbmUgYSBsYSBjYWJlemEgZXMgaGFjZXIgbG8gc2lndWllbnRlOgoKClByaW1lcm8sIGNyZWFyIHVuIGRhdGFzZXQgcXVlIHPDs2xvIGNvbnRlbmdhIGEgbG9zIGxpcmlvcyBncmFuZGVzLCBsb3MgZGUgbGFzIGVzcGVjaWVzIHZpcmdpbmljYSB5IHZlcnNpY29sb3IuCgpgYGB7cn0KaXJpczIgPC0gaXJpcyAlPiUgZmlsdGVyKFNwZWNpZXMgIT0gInNldG9zYSIpICMtIG1lIHF1ZWRvIGNvbiBsb3MgbGlyaW9zIHF1ZSBubyBzb24gZGUgY2xhc2UgInNldG9zYSIKYGBgCgpQYXJhIGRlc3B1w6lzIGhhY2VyIGVsIGdyw6FmaWNvIGNvbiBjdWFscXVpZXJhIGRlIGxhcyAyIGV4cHJlc2lvbmVzIHNpZ3VpZW50ZXMuIFByZWZpZXJvIGxhIHNlZ3VuZGEgcG9ycXVlIGhheSBxdWUgdGVjbGVhci9lc2NyaWJpciBtZW5vcywgcGVybyBwdWVkZSBxdWUgc2VhIG3DoXMgZGlkw6FjdGljYSBsYSBwcmltZXJhLgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KCkgKyBnZW9tX3BvaW50KGRhdGEgPSBpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykpICsgZ2VvbV9zbW9vdGgoZGF0YSA9IGlyaXMyLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpICkgCgpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IFNwZWNpZXMpKSArIGdlb21fc21vb3RoKGRhdGEgPSBpcmlzMikKYGBgCgo8YnI+CgoqKk90cm8gZWplbXBsbyoqIG3DoXM6IMK/eSBzaSBxdWlzacOpcmFtb3MgcXVlIGxhcyAyIGVzcGVjaWVzIGdyYW5kZXMgc2UgcmVwcmVzZW50ZW4gY29uIGVsIG1pc21vIGNvbG9yPyBIYXkgdmFyaWFzIHNvbHVjaW9uZXMsIHVuYSBkZSBsYXMgbcOhcyBtYXJjaWFuYXMgZXMgbGEgcXVlIHByb3BvbmdvIGFiYWpvLiBFcyB1bmEgc29sdWNpw7NuIHJhcmEsIHBlcm8gY3JlbyBxdWUgb3MgYXl1ZGFyw6EgYSBlbnRlbmRlciBgZ2dwbG90MmAKClByaW1lcm8gdm95IGEgY3JlYXIgdW4gbnVldm8gZGF0YS5mcmFtZSBzw7NsbyBjb24gbGFzIG9ic2VydmFjaW9uZXMgZGUgbG9zIGxpcmlvcyBwZXF1ZcOxb3MsIGxvcyBkZSBsYSBjbGFzZSBzZXRvc2EuCgpgYGB7cn0KaXJpc19zZXRvc2EgPC0gaXJpcyAlPiUgZmlsdGVyKFNwZWNpZXMgPT0gInNldG9zYSIpICMtIG1lIHF1ZWRvIGNvbiBsb3MgbGlyaW9zIHBlcXVlw7FvcywgbG9zIGRlIGNsYXNlICJzZXRvc2EiCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3BvaW50KGRhdGEgPSBpcmlzX3NldG9zYSwgYWVzKGNvbG9yID0gU3BlY2llcykpICsgZ2VvbV9zbW9vdGgoZGF0YSA9IGlyaXMyLGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkgKQpgYGAKCk90cmEgc29sdWNpw7NuLCBxdWl6w6FzIG3DoXMgbMOzZ2ljYSwgY29uc2lzdGUgZW4gcHJpbWVybyBhZ3J1cGFyIGxhcyAyIGVzcGVjaWVzIGRlIGxpcmlvcyBncmFuZGVzICh2ZXJzaWNvbG9yIHkgdmlyZ2luaWNhKSBlbiB1bmEgc29sYSBjbGFzZS4KCgpgYGB7cn0KaXJpc19zb2xvXzJfY2xhc2VzIDwtIGlyaXMgJT4lIG11dGF0ZShTcGVjaWVzXzIgPSBpZmVsc2UoU3BlY2llcyAlaW4lIGMoInZlcnNpY29sb3IiLCAidmlyZ2luaWNhIiksICJ2ZXJzaV92aXJnaSIsICJzZXRvc2EiKSkKYGBgCgpQYXJhIGRlc3B1w6lzIGhhY2VyIGVsIGdyw6FmaWNvLiBBZGVtw6FzIGVsIGdyw6FmaWNvIGxvIHBvZGVtb3MgaGFjZXIgYWwgbWVub3MgZGUgMiBtYW5lcmFzLCBsYSBzZWd1bmRhIG11Y2hvIG1lam9yLCBsYSBwcmltZXJhIGV4cHJlc2nDs24gZXMgdW4gcG9jbyBlbnJldmVzYWRhOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9wb2ludChkYXRhID0gaXJpc19zZXRvc2EsIGFlcyhjb2xvciA9IFNwZWNpZXMpKSArIGdlb21fc21vb3RoKGRhdGEgPSBpcmlzX3NvbG9fMl9jbGFzZXMsYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBjb2xvciA9IFNwZWNpZXNfMikgKQoKZ2dwbG90KGlyaXNfc29sb18yX2NsYXNlcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBjb2xvciA9IFNwZWNpZXNfMikpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKQpgYGAKCgpgYGB7ciwgZWNobyA9IEZBTFNFfQpnZ3Bsb3QoaXJpc19zb2xvXzJfY2xhc2VzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llc18yKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpCmBgYAoKCkNvbW8gdmVpcywgZW4gYGdncGxvdDJgIGhheSB2YXJpYXMgbWFuZXJhcyBkZSBoYWNlciBlbCBtaXNtbyBncsOhZmljby4gRXN0byBhbCBwcmluY2lwaW8gcHVlZGUgYWJydW1hci9tb2xlc3RhciwgcGVybyBtdWVzdHJhIGxhIGZsZXhpYmlsaWRhZCBkZSBsYSBzaW50YXhpcy4KCgpQYXJhIGlyIGFjYWJhbmRvIGNvbiBsYSAiZmlsb3NvZsOtYSIvc2ludGF4aXMvZ3JhbcOhdGljYSBkZSBgZ2dwbG90MmAgaW50ZW50YSBpbWFnaW5hciBxdWUgZ3LDoWZpY29zIGhhY2VuIGxhcyA2IGV4cHJlc2lvbmVzIGRlIG3DoXMgYWJham8uCgpTaSBubyBwdWVkZXMsIHJlY3VlcmRhIHF1ZSBzaWVtcHJlIHB1ZWRlcyBlamVjdXRhciBsYXMgb3JkZW5lcyBlbiBlbCBvcmRlbmFkb3IuIEbDrWphdGUgc29icmUgdG9kbyBlbiBsYSB0ZXJjZXJhIGV4cHJlc2nDs24KCmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkKZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpCmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykpICsgZ2VvbV9wb2ludChjb2xvciA9ICJwdXJwbGUiKSArIGdlb21fc21vb3RoKCkKCmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoY29sb3IgPSAiYnJvd24iKQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBTcGVjaWVzKSkKZ2dwbG90KGlyaXMpICsgZ2VvbV9wb2ludChhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykgKSArIGdlb21fc21vb3RoKGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkKYGBgCgo8YnI+CgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiMTAwJSJ9CnAxIDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkKcDIgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpCnAzIDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykpICsgZ2VvbV9wb2ludChjb2xvciA9ICJwdXJwbGUiKSArIGdlb21fc21vb3RoKCkKcDQgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChjb2xvciA9ICJicm93biIpCnA1IDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IFNwZWNpZXMpKQpwNiA8LSBnZ3Bsb3QoaXJpcykgKyBnZW9tX3BvaW50KGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSApICsgZ2VvbV9zbW9vdGgoYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBjb2xvciA9IFNwZWNpZXMpKQoKcDEgKyBwMiArIHAzICsgcDQgKyBwNSArIHA2ICsgcGxvdF9sYXlvdXQobmNvbCA9IDIpCmBgYAoKCgpFbiBsYSB0ZXJjZXJhIGV4cHJlc2nDs24gc2UgZXNwZWNpZmljYSBgY29sb3IgPSBTcGVjaWVzYCBkZW50cm8gZGUgYGFlcygpYCBlbiBgZ2dwbG90KClgLCBhc8OtIHF1ZSwgZGUgbW9tZW50bywgdG9kb3MgbG9zIGdlb21zIGRlbCBncsOhZmljbyBkZWJlcsOtYW4gZGlmZXJlbmNpYXIgcG9yIGVzcGVjaWVzIGRlIGxpcmlvcyB1c2FuZG8gZWwgY29sb3IsIFBFUk8sIGRlc3B1w6lzIHNlIHZ1ZWx2ZSBhIHVzYXIgZWwgYXJndW1lbnRvIGBjb2xvcmAgZGVudHJvIGRlIGBnZW9tX3BvaW50KClgLCBwZXJvIGbDrWphdGUgcXVlIG5vIHZhIGRlbnRybyBkZSBkZSBgYWVzKClgLCB2YSBmdWVyYS4gQ29uY3JldGFtZW50ZSBoYWNlbW9zIGxvIHNpZ3VpZW50ZTogYGdlb21fcG9pbnQoY29sb3IgPSAicHVycGxlIilgOyBlcyBkZWNpciwgcGFyYSBsYSBjYXBhIGRlIHB1bnRvcywgeSBzb2xvIHBhcmEgbGEgY2FwYSBkZSBwdW50b3MgcXVlIHNlIGNyZWEgY29uIGBnZW9tX3BvaW50KClgLCBlc3RhbW9zIGFzb2NpYW5kbyBsYSBlc3TDqXRpY2EgY29sb3IsIG5vIGEgdW5hIHZhcmlhYmxlLCBzaW5vIGEgdW4gY29sb3IgZmlqby4gU2luIGVtYmFyZ28sIHBhcmEgbGEgb3RyYSBjYXBhIGRlbCBncsOhZmljbywgbGEgcXVlIHJlc3VsdGEgZGUgdXNhciBgZ2VvbV9zbW9vdGgoKWAgc2lndWUgc2llbmRvIHZhbGlkbyBxdWUgbGEgZXN0w6l0aWNhIGNvbG9yIGVzdMOhIGFzb2NpYWRhIGEgbGEgdmFyaWFibGUgU3BlY2llcy4gCgoKVW4gZGV0YWxsZTogaW1hZ2luYSBxdWUgZW4gdW4gZ3LDoWZpY28gZW4gZWwgcXVlIGhhcyBmaWphZG8gMyBlc3TDqXRpY2FzIGRlbnRybyBkZSBgZ2dwbG90KGFlcygpKWAuIEVuIHByaW5jaXBpbyBsYXMgMyBlc3TDqXRpY2FzIGFmZWN0YXLDoW4gYSB0b2RvcyBsb3MgYGdlb21feHgoKWAgcXVlIHV0aWxpY2VzIGVuIHR1IGdyw6FmaWNvLCBwZXJvIHNpIHF1aXNpZXJhcyBxdWUsIHBvciBlamVtcGxvLCBsYSBlc3TDqXRpY2EgY29sb3Igbm8gYWZlY3Rhc2UgYSB1biBnZW9tIGNvbmNyZXRvLCBwb2Ryw61hcyBoYWNlciBsbyBzaWd1aWVudGU6IGBnZW9tX3h4KGFlcyhjb2xvciA9IE5VTEwpKWAuICAgCgoKQ29tbyBwdWVkZXMgaW1hZ2luYXIsIGHDum4gdGVuZW1vcyBxdWUgdmVyIG3DoXMgZWxlbWVudG9zIGRlIGBnZ3Bsb3QyYC4gQ29tbyBtw61uaW1vIGxvcyB0w610dWxvcyB5IGxleWVuZGFzLCBsb3MgZWplcywgZWwgdGVtYSwgY29vcmRlbmFkYXMsIGV0Yy4uLiB2YW1vcyBhIGVsbG8hIQoKPGJyPgoKCiMjIDMuIEVsZW1lbnRvcyBkZSB1biBnZ3Bsb3QKCgpZYSBoZW1vcyBwcmVzZW50YWRvIGxvcyBwcmluY2lwYWxlcyBlbGVtZW50b3MgZGUgbG9zIGdyw6FmaWNvcyBoZWNob3MgY29uIGBnZ3Bsb3QyYCwgbG9zIHF1ZSB0aWVuZW4gcXVlIHZlciBjb24gbGEgcmVwcmVzZW50YWNpw7NuIGRlIGxhcyB2YXJpYWJsZXMuIFBlcm8gZXMgZXZpZGVudGUgcXVlIHVuIGdyw6FmaWNvIHRpZW5lIG11Y2hvcyBtw6FzIGVsZW1lbnRvcywgeSBsw7NnaWNhbWVudGUgaGF5IHF1ZSBjb25vY2VybG9zIHVuIHBvY28gIHBhcmEgcG9kZXIgYWp1c3RhciBsb3MgZ3LDoWZpY29zIGEgbnVlc3RyYXMgbmVjZXNpZGFkZXMgeSBtZWpvcmFyIGxhIGNhbGlkYWQgZGUgbnVlc3Ryb3MgZ3LDoWZpY29zLiAKCkVqZW1wbG9zIGRlIG90cm9zIGVsZW1lbnRvcyBzb246IHTDrXR1bG9zIGRlbCBncsOhZmljbyB5IGRlIGxvcyBlamVzLCAidGhlbWUiIGRlbCBncsOhZmljbywgc21hbGwgbXVsdGlwbGVzIG8gZmFjZXRpbmcsIGFub3RhY2lvbmVzIGV0Yy4uLgoKRW4gZXN0w6Egc2VjY2nDs24gaXJlbW9zIG3DoXMgcsOhcGlkby4gU2UgcHJlc2VudGFyw6FuIHNvbGFtZW50ZSBhbGd1bm9zIGVqZW1wbG9zLCBjb25jZXB0b3MgeS9vIGFjbGFyYWNpb25lcy4gU2kgbmVjZXNpdGFzIHByb2Z1bmRpemFyIG3DoXMgZW4gZXN0b3MgZWxlbWVudG9zLCBwdWVkZXMgYWN1ZGlyIGEgbGEgW3JlZmVyZW5jaWEgb2ZpY2lhbF0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2luZGV4Lmh0bWwjc2VjdGlvbi1sYXllci1nZW9tcykgZGUgYGdncGxvdDJgIG8gYWwgW2Jva2tkb3duIGRlIGdncGxvdDJdKGh0dHBzOi8vZ2dwbG90Mi1ib29rLm9yZy8pLgoKCllhIGRpamltb3MgcXVlIGxvcyBncsOhZmljb3MgZ2dwbG90IHNlIGNvbXBvbmVuIGRlIGNhcGFzIG8gbGF5ZXJzLiBQYXJhIG5vc290cm9zLCBoYXN0YSBhaG9yYSwgdW5hIGNhcGEgZXN0YWJhIGNvbXB1ZXN0YSBkZSAzIGVsZW1lbnRvczoKCiAgLSB1biBjb25qdW50byBkZSBkYXRvcyAgCiAgCiAgLSB1biBjb25qdW50byBkZSB2YXJpYWJsZXMgbWFwZWFkYXMgY29uIGBhZXMoKWAgYSBwcm9waWVkYWRlcyBlc3TDqXRpY2FzICAKICAKICAtIHVuYSBnZW9tZXRyw61hLCBjb24gYGdlb21feHgoKWAgCiAgCkVzIGV2aWRlbnRlIHF1ZSBlbiB0b2RvcyBsb3MgZ2VvbXMgbm8gc2UgcHVlZGVuIGVzcGVjaWZpY2FyIHRvZGFzIGxhcyBjYXJhY3RlcsOtc3RpY2FzIGVzdMOpdGljYXMuIFBvciBlamVtcGxvIHNpIHVzYXMgYGdlb21fcG9pbnRgIG5vIHBvZHLDoXMgZXNwZWNpZmljYXIgbGEgYW5jaHVyYSBvIGVsIHRpcG8gZGUgbGFzIGxpbmVhcywgcG9ycXVlIG5vIGVzdMOhcyB1c2FuZG8gbGluZWFzIHNpbm8gcHVudG9zLiBQYXJhIHZlciBxdWUgZXN0w6l0aWNhcyBhZG1pdGUgY2FkYSBnZW9tIHRlbmRyw6FzIHF1ZSBtaXJhciBsYSBheXVkYSBkZSBjYWRhIGdlb20uIEFsIGZpbmFsIGRlIFtlc3RlIHBvc3RdKGh0dHBzOi8vd3d3LnlpaGFud3UuY2EvcG9zdC9nZW9tcy1hbmQtYWVzdGhldGljLXBhcmFtZXRlcnMvKSB0aWVuZW4gdW4gZ3LDoWZpY28gaW50ZXJhY3Rpdm8gY29uIGVsIHF1ZSBzZSBwdWVkZSAgdmVyIGbDoWNpbG1lbnRlIHF1ZSBjYXJhY3RlcsOtc3RpY2FzIGVzdMOpdGljYXMgYWRtaXRlIGNhZGEgZ2VvbS4gUG9yIGVqZW1wbG8gYGdlb21fYmFyKClgLCBxdWUgc2lydmUgcGFyYSBoYWNlciBncsOhZmljb3MgZGUgYmFycmFzLCBubyBhZG1pdGUgbWFwcGVhciB2YXJpYWJsZXMgYWwgZWplIFksIHlhIHF1ZSBlbiBlbCBlamUgWSBzZSB2aXN1YWxpemFuL21hcGVhbiBsYXMgZnJlY3VlbmNpYXMgYWJzb2x1dGFzIG8gcmVsYXRpdmFzIGRlIGxhIHZhcmlhYmxlIHF1ZSBzZSByZXByZXNlbnRhIGVuIGVsIGVqZSBYLgoKPGJyPgoKRXN0byBlcyBsbyBiw6FzaWNvIHF1ZSBoYXkgcXVlIHNhYmVyLCBwZXJvIGVuIHJlYWxpZGFkLCB1bmEgY2FwYSBuZWNlc2l0YSBkZSBkb3MgZWxlbWVudG9zIG3DoXM6IHVuYSAqKnN0YXQqKiAobyB0cmFuc2Zvcm1hY2nDs24gZXN0YWTDrXN0aWNhKSB5IHVuYSAqKnBvc2ljacOzbioqLiAgRXN0b3MgZG9zIMO6bHRpbW9zIGVsZW1lbnRvcyBzb24gbmVjZXNhcmlvcyBwZXJvIGxhIHZlcmRhZCBlcyBxdWUgcG9kcsOtYW1vcyBzZWd1aXIgaGFjaWVuZG8gZ3LDoWZpY29zIGNvbiBgZ2dwbG90MmAgc2luIGNvbm9jZXJsb3MuIMK/UG9yIHF1w6k/IFB1ZXMgcG9ycXVlIHNpIGVuIHVuYSBjYXBhIG5vIGxvcyBlc3BlY2lmaWNhbW9zLCBsbyBoYWNlIGBnZ3Bsb3QyYCBwb3Igbm9zb3Ryb3MuIExvIGhhIGVzdGFkbyBoYWNpZW5kbyBoYXN0YSBhaG9yYSBlbiB0b2RvcyBsb3MgZ3LDoWZpY29zIHF1ZSBsbGV2YW1vcyBoZWNob3MuIFBlcm8gY2xhcm8sIHNhYmVyIGNvbW8gdXRpbGl6YXIgZXN0b3MgZWxlbWVudG9zIG5vcyBkYXLDoSBtw6FzIGZsZXhpYmlsaWRhZCBhIGxhIGhvcmEgZGUgaGFjZXIgZ2dwbG90cy4KCgpHZW5lcmFsbWVudGUgbGFzIGNhcGFzIHNlIHZhbiBhw7FhZGllbmRvIGNvbiBsYSBmYW1pbGlhIGRlIGZ1bmNpb25lcyBgZ2VvbV94eCgpYCwgKipQRVJPKiogdGFtYmnDqW4gc2UgcHVlZGVuIGHDsWFkaXIgY2FwYXMgY29uIG90cmEgZmFtaWxpYSBkZSBmdW5jaW9uZXMgYHN0YXRfeHgoKWAuIAoKCkFwYXJ0ZSBkZSBlc3RvcyBjaW5jbyBlbGVtZW50b3MgKGRhdG9zLCBhZXMoKSwgZ2VvbSwgc3RhdCB5IHBvc2ljacOzbikgbG9zIGdyw6FmaWNvcyBnZ3Bsb3QgcHVlZGVuIHRlbmVyIG3DoXMgZWxlbWVudG9zLiBWZcOhbW9zbG9zIHVubyBhIHVuby4KCjxicj4KCiMjIyAzLjEgVMOtdHVsb3MgZGVsIGdyw6FmaWNvCgpFcyBldmlkZW50ZSBxdWUgdW4gZ3LDoWZpY28gcGFyYSBzZXIgZWZlY3Rpdm8geSBtb3N0cmFyIHN1IG1lbnNhamUgY29uIGNsYXJpZGFkIGRlYmUgdGVuZXIgdW4gdMOtdHVsbyB5L28gc3VidMOtdHVsbyBpbHVzdHJhdGl2byB5IGRlYmUgbW9zdHJhciBpbmZvcm1hY2nDs24gcmVsZXZhbnRlIHNvYnJlIHF1ZSB2YXJpYWJsZXMgc2UgZ3JhZmljYW4gbG9zIGVqZXMgWCBlIFkuIEVzdGUgdGlwbyBkZSBlbGVtZW50b3MgcHVlZGVuIG1vZGlmaWNhcnNlIGRlIHZhcmlhcyBtYW5lcmFzLCBwZXJvIG5vcyBjZW50cmFyZW1vcyBlbiBsYSBmdW5jacOzbiBgbGFicygpYC4gCgpGw61qYXRlIHF1ZSBjb24gbGEgZnVuY2nDs24gYGxhYnMoKWAsIGRlIGxhYmVscywgcG9kZW1vcyBjYW1iaWFyIGxvcyB0w610dWxvcyBkZWwgZ3LDoWZpY28sIGRlIGxvcyBlamVzIHkgdGFtYmnDqW4gZGUgbGFzIGxleWVuZGFzLiAKCkVuIGxvcyB0w610dWxvcyAodGFudG8gZGVsIGdyw6FmaWNvLCBjb21vIGRlIGxvcyBlamVzIHkgbGV5ZW5kYXMpIHRhbWJpw6luIHNlIHB1ZWRlbiBjYW1iaWFyIG90cmFzIGNhcmFjdGVyw61zdGljYXM7IHBvciBlamVtcGxvLCBjYW1iaWFyIGVsIHRhbWHDsW8sIGxhIGZ1ZW50ZSBvIGVsIGNvbG9yLCBwZXJvIGVzbyBzZXLDoSB0YXJlYSBkZSBvdHJhIGZ1bmNpw7NuIGRlIGBnZ3Bsb3QyYDogZGVsIGdydXBvIGRlIGZ1bmNpb25lcyBgdGhlbWVfKClgLiBQZXJvIGVsIHRlbWEgbyB0aGVtZSBkZSBsb3MgZ3LDoWZpY29zIGxvIHZlcmVtb3MgZW4gZWwgc2lndWllbnRlIGFwYXJ0YWRvLgoKClRvbWVtb3MgZWwgc2lndWllbnRlIGdyw6FmaWNvIGNvbW8gcmVmZXJlbmNpYSB5IHNvYnJlIMOpbCBpcmVtb3MgYcOxYWRpZW5kbyBlbGVtZW50b3M6CgpgYGB7cn0KcCA8LSBnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBjb2xvciA9IFNwZWNpZXMpKSArIGdlb21fcG9pbnQoKQpwCmBgYAoKQ29uIGxhIGZ1bmNpw7NuIGBsYWJzKClgIHBvZGVtb3MgYcOxYWRpcmxlIHVuIHTDrXR1bG8sIHN1YnRpdHVsbywgcGllIGRlIGdyw6FmaWNvIG8gKmNhcHRpb24qLiBUYW1iacOpbiBwb2RlbW9zIGNhbWJpYXIgZWwgdMOtdHVsbyBkZSBsb3MgZWplcyBYIGUgWSwgYXPDrSBjb21vIHRhbWJpw6luIGVsIHRpdHVsbyBkZSBsYSBsZXllbmRhIHBhcmEgYGNvbG9yYCwgbyBwYXJhIG90cmFzIGVzdMOpdGljYXMgcXVlIHV0aWxpY2Vtb3MgZW4gZWwgZ3LDoWZpY28uCgpFcyBzdWZpY2llbnRlIGNvbiB2ZXIgdW4gZWplbXBsbzoKCgpgYGB7cn0KcCArIGxhYnModGl0bGUgPSAiR3LDoWZpY28gMTogTG9uZ2l0dWQgZGVsIHPDqXBhbG8gZnJlbnRlIGFsIHDDqXRhbG8iLAogICAgICAgc3VidGl0bGUgPSAiKGRpZmVyZW5jaWFuZG8gcG9yIGVzcGVjaWUgZGUgbGlyaW8pIiwKICAgICAgIGNhcHRpb24gPSAiRGF0b3MgcHJvdmVuaWVudGVzIGRlbCBJcmlzIGRhdGFzZXQiLAogICAgICAgeCA9ICJMb25naXR1ZCBkZWwgc8OpcGFsbyIsCiAgICAgICB5ID0gIkxvbmdpdHVkIGRlbCBww6l0YWxvIiwKICAgICAgIGNvbG9yID0gIkVzcGVjaWUgZGUgbGlyaW8iKQpgYGAKCgpTaSBxdWlzaWVyYXMgZWxpbWluYXIgY29tcGxldGFtZW50ZSBsb3MgKip0w610dWxvcyoqICBkZWwgZWplIFggcG9kcsOtYXMgaGFjZXJsbyBlbiBlbCBhbnRlcmlvciBjaHVuayBmaWphbmRvIGB4ID0gTlVMTGAgZGVudHJvIGRlIGxhIGZ1bmNpw7NuIGBsYWJzKClgLgoKCkVuIGx1Z2FyIGRlIHVzYXIgbGEgZnVuY2nDs24gYGxhYnMoKWAsIHRhbWJpw6luIHBvZGVtb3MgdXRpbGl6YXIgbGFzIGZ1bmNpb25lcyBhdXhpbGlhcmVzIGB4bGFiKClgIGUgYHlsYWIoKWAKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwICsgbGFicyhjb2xvciA9IE5VTEwsIHggPSBOVUxMKSAgIy0gYm9ycmEgZWwgdMOtdHVsbyBkZSBsYSBsZXllbmRhIHkgZGVsIGVqZSBYCnAgKyB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSAgICAgICAjLSBlbGltaW5hIHTDrXR1bG9zIGRlIGxvcyBlamVzIFggZSBZCmBgYAoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICI5MCUiLCAgZmlnLmFzcCA9IDMvOX0KcDEgPC0gcCArIGxhYnMoY29sb3IgPSBOVUxMLCB4ID0gTlVMTCkgICMtIGJvcnJhIGVsIHTDrXR1bG8gZGUgbGEgbGV5ZW5kYSB5IGRlbCBlamUgWApwMiA8LSBwICsgeGxhYihOVUxMKSArIHlsYWIoTlVMTCkgICAgICAgIy0gZWxpbWluYSB0w610dWxvcyBkZSBsb3MgZWplcyBYIGUgWQoKcDEgKyBwMiArIHBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCgoKCiMjIyAzLjIgVGhlbWVzCgo+IFRoZW1lcyBjb250cm9sIHRoZSBkaXNwbGF5IG9mIGFsbCBub24tZGF0YSBlbGVtZW50cyBvZiB0aGUgcGxvdC4gWW91IGNhbiBvdmVycmlkZSBhbGwgc2V0dGluZ3Mgd2l0aCBhIGNvbXBsZXRlIHRoZW1lIGxpa2UgdGhlbWVfYncoKSwgb3IgY2hvb3NlIHRvIHR3ZWFrIGluZGl2aWR1YWwgc2V0dGluZ3MgYnkgdXNpbmcgdGhlbWUoKSBhbmQgdGhlIGVsZW1lbnRfIGZ1bmN0aW9ucy4gVXNlIHRoZW1lX3NldCgpIHRvIG1vZGlmeSB0aGUgYWN0aXZlIHRoZW1lLCBhZmZlY3RpbmcgYWxsIGZ1dHVyZSBwbG90cy4KClBhcmEgY2FtYmlhciBkZXRhbGxlcyBkZSBsYSBhcGFyaWVuY2lhIGRlbCBncsOhZmljbyBjb21vIGVsIHRhbWHDsW8sIGZ1ZW50ZXMgeSBjb2xvciBkZSBsb3MgdMOtdHVsb3MsIHBlcm8gdGFtYmnDqW4gZGUgbG9zIHB1bnRvcywgbGFzIGxpbmVhcywgZWwgZm9uZG8gZGVsIGdyw6FmaWNvLCBsYSBhcGFyaWVuY2lhIGRlIGxhcyBncmlkLWxpbmVzLCBlbCBsdWdhciBwYXJhIGxhcyBsZXllbmRhcywgZXRjLi4uIGV0Yy4uLiBjb250YW1vcyBjb24gbGFzICJmdW5jaW9uZXMgZGUgdGVtYSI7IHRvZGFzIGVsbGFzIGNvbWllbnphbiBjb24gYHRoZW1lXygpYAoKRW4gZ2VuZXJhbCBjb24gbGFzIGZ1bmNpb25lcyBgdGhlbWVfKClgIHBvZGVtb3MgY2FtYmlhci9hanVzdGFyIGN1YWxxdWllciBlbGVtZW50byBkZWwgZ3LDoWZpY28sIGNvbiBsYSBleGNlcGNpw7NuIGRlIGxhIHByb3BpYSByZXByZXNlbnRhY2nDs24gZGUgbG9zIGRhdG9zICh5YSBzYWJlbW9zIHF1ZSBlc3RvIHNlIGhhY2VuIGNvbiBsYXMgZnVuY2lvbmVzIGBnZW9tXygpYCkuIEVzdG9zIGVsZW1lbnRvcyBhZmVjdGFuIGEgbGEgYXBhcmllbmNpYSB5IGRldGFsbGVzIGRlbCBncsOhZmljbywgcGVybyBubyBhIGxhIHJlbGFjacOzbiBlbnRyZSB2YXJpYWJsZXMgcXVlIHNlIG11ZXN0cmEgcmVhbG1lbnRlIGVuIGVsIGdyw6FmaWNvLgoKClBhcmEgZW1wZXphciBhIGVudGVuZGVyIHF1ZSBoYWNlbiBsYXMgZnVuY2lvbmVzIHJlbGFjaW9uYWRhcyBjb24gZWwgdGhlbWUsIHNlw7FhbGFyIHF1ZSBgZ2dwbG90MmAgaW5jb3Jwb3JhIHVuIGNvbmp1bnRvIGRlICJ0ZW1hcyIgcXVlIHBvZGVtb3MgdXRpbGl6YXIgcGFyYSBjYW1iaWFyIGxhIGFwYXJpZW5jaWEgZGVsIGdyw6FmaWNvIGEgbnVlc3RybyBndXN0by4gUHVlZGVzIHZlcmxvcyB0b2RvcyBbYXF1w61dKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZ3RoZW1lLmh0bWwpLiBFbCB0ZW1hIHF1ZSB1c2EgcG9yIGRlZmVjdG8gYGdncGxvdDJgIGVzIGB0aGVtZV9ncmF5KClgLiBWZWFtb3MgYSBsb3MgdGhlbWVzIGVuIGFjY2nDs246CgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CnAgKyB0aGVtZV9ncmF5KCkgICAjLSB0ZW1hIHBvciBkZWZlY3RvCnAgKyB0aGVtZV9saWdodCgpCnAgKyB0aGVtZV9kYXJrKCkKcCArIHRoZW1lX2NsYXNzaWMoKQpwICsgdGhlbWVfbWluaW1hbCgpCnAgKyB0aGVtZV92b2lkKCkKYGBgCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjkwJSIsICBmaWcuYXNwID0gNS85fQpwMSA8LSBwICsgdGhlbWVfZ3JheSgpICAgIy0gdGVtYSBwb3IgZGVmZWN0bwpwMiA8LSBwICsgdGhlbWVfbGlnaHQoKQpwMyA8LSBwICsgdGhlbWVfZGFyaygpCnA0IDwtIHAgKyB0aGVtZV9jbGFzc2ljKCkKcDUgPC0gcCArIHRoZW1lX21pbmltYWwoKQpwNiA8LSBwICsgdGhlbWVfdm9pZCgpCgpwMSArIHAyICsgcDMgKyBwNCArIHA1ICsgcDYgKyBwbG90X2xheW91dChuY29sID0gMikKYGBgCgoKCkVsIHBhcXVldGUgZGUgUiBbZ2d0aGVtZXNdKGh0dHBzOi8vanJub2xkLmdpdGh1Yi5pby9nZ3RoZW1lcy9yZWZlcmVuY2UvaW5kZXguaHRtbCkgaW5jb3Jwb3JhIHVuYSBhbXBsaWEgbGlzdGEgZGUgdGVtYXMgYWRpY2lvbmFsZXMsIGFsZ3Vub3MgZGUgZWxsb3MgdHJhdGFuIGRlIHJlcGxpY2FyIGVsIGVzdGlsbyBkZSBjb3Jwb3JhY2lvbmVzIGZhbW9zYXMgY29tbyBUaGUgRWNvbm9taXN0IG8gU3RhdGEuIEVuIGVsIHR1dG9yaWFsIG5vIHNlIHZlbiBiaWVuIGxvcyBncsOhZmljb3MgcG9ycXVlIGxvcyBoZSBoZWNobyBwZXF1ZcOxaXRvcywgcGVybyBwcnVlYmEgYSBoYWNlcmxvcyB0w7ogbWlzbW8geSB2ZXLDoXMgcXVlIHNvbiBnb29kLWxvb2tpbmcuIFZlYW1vcyBhbGd1bm9zOgoKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KGdndGhlbWVzKQpwICsgdGhlbWVfZWNvbm9taXN0KCkgICAKcCArIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpCnAgKyB0aGVtZV9zdGF0YSgpCnAgKyB0aGVtZV9zb2xhcml6ZWQoKQpgYGAKCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjg1JSIsICBmaWcuYXNwID0gNS85fQpsaWJyYXJ5KGdndGhlbWVzKQpwMSA8LSBwICsgdGhlbWVfZWNvbm9taXN0KCkgCnAyIDwtIHAgKyB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKQpwMyA8LSBwICsgdGhlbWVfc3RhdGEoKSAgCnA0IDwtIHAgKyB0aGVtZV9zb2xhcml6ZWQoKQoKcDEgKyAgcDIgKyBwbG90X2xheW91dChuY29sID0gMikKcDMgKyAgcDQgKyBwbG90X2xheW91dChuY29sID0gMikKCgpgYGAKCgoKClRhbWJpw6luIHBvZGVtb3MgZGVmaW5pciB1biB0ZW1hIHByb3BpbyBwYXJhIHF1ZSBlbCBncsOhZmljbyBzZSBhanVzdGUgbG9zIG3DoXMgcG9zaWJsZSBhIG51ZXN0cmFzIHByZWZlcmVuY2lhcy4KCgpgYGB7ciwgb3V0LndpZHRoID0gIjUwJSIsICBmaWcuYXNwID0gNC85fQojIGRlZmluZSBjdXN0b20gdGhlbWUKbXlfdGhlbWUgPC0gdGhlbWUoYXhpcy50ZXh0LnggPSAKICAgICAgICAgICAgICAgICAgZWxlbWVudF90ZXh0KGNvbG91ciA9ICJncmV5MjAiLCBzaXplID0gMTIsIGFuZ2xlID0gOTAsIGhqdXN0ID0gMC41LCB2anVzdCA9IDAuNSkgLCAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiZ3JleTIwIiwgc2l6ZSA9IDEyKSAsCiAgICAgICAgICAgICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSkKcCArIG15X3RoZW1lCmBgYAoKClNlIHB1ZWRlIGZpamFyIGVsIHRlbWEvdGhlbWUgZGUgbG9zIGdyw6FmaWNvcyBjb24gbGEgZnVuY2nDs24gYHRoZW1lX3NldCgpYC4gUG9yIGVqZW1wbG86CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpKSAgIy0gdW4gdGVtYSBjb25jcmV0bwp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpICsgCiAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpKSAjLSB1biB0ZW1hIG1vZGlmaWNhbmRvIGFsZ3VuYXMgb3BjaW9uZXMsIHF1ZSBlbGUgZWplIHggbm8gbXVlc3RyZSB0aWNrcyBuaSBlc2NhbGFzCmBgYAoKU2kgcXVpZXJlcyB2b2x2ZXIgYWwgdGhlbWUgcG9yIGRlZmVjdG86CgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KdGhlbWVfc2V0KHRoZW1lX2dyYXkoKSkKYGBgCgoKRWplbXBsb3MgZGUgYWxndW5vcyBlbGVtZW50b3MgY3V5YSBhcGFyaWVuY2lhIHF1ZSBzZSBwdWVkZW4gY2FtYmlhciBjb24gYHRoZW1lKClgCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAgICAgICAgICAgICMtIHF1ZSBubyBhcGFyZXpjYSBsZXllbmRhCnAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgICAgICAgICAgIy0gbGV5ZW5kYSBhYmFqbwpwICsgdGhlbWUobGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikgICAgICMtIGxleWVuZGEgaG9yaXpvbnRhbCEhCnAgKyB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIyKSkgICAgICMtIHTDrXR1bG8gZGUgbGEgbGV5ZW5kYSBhIDIyCnAgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDIuNCwgImNtIikpICAgICAgICAgICMtIHRhbWHDsW8gZGUgbG9zIGN1YWRyb3MgZGUgbGEgbGV5ZW5kYQoKCnAgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCwgZmFjZSA9ICJib2xkIikpICAgICAgICAgIy0gY2FtYmlhciBlbCB0YW1hw7FvIGRlIHRvZG9zIGxvcyBlbGVtZW50b3MgZGUgdGV4dG8KcCArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIikpICAgICAgICAgICAgICAgICAgICAjLSBwb25lIGVuIG5lZ3JpdGEgdG9kb3MgbG9zIGVsZW1lbnRvcyBkZSB0ZXh0bwoKcCArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJwaW5rIiwgc2l6ZSA9IDEyLCBhbmdsZSA9IDkwLCBoanVzdCA9IDAuNSwgdmp1c3QgPSAwLjUpKSAjIGFwYXJpZW5jaWEgZGUgbGEgZXNjYWxhIGRlbCBlamUgeAoKcCArIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTI1LCBhbmdsZSA9IDQ1KSkgIy0gdGFtYcOxbyB5IGFuZ3VsbyBkZWwgdGV4dG8gZGVsIGVqZSBZCgpwICsgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDMpKSAgICMtIHBvc2ljacOzbiBob3Jpem9udGFsIGRlbCBzdWJ0aXR1bG8gKHNpIGxvIHR1dmllc2UpCiAgCnAgKyB0aGVtZShwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAzKSkgICAgIy0gcG9zaWNpw7NuIHZlcnRpY2FsIGRlbCBwaWUgZGUgZ3LDoWZpY28gKHNpIGxvIHR1dmllc2UpCgpwICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyZWVuIiwgY29sb3VyID0gInBpbmsiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIsIHNpemUgPSAzLjUpKQpwICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkKcCArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBOVUxMKQoKCnAgKyB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJwaW5rIiwgY29sb3VyID0gInB1cnBsZSIsIGxpbmV0eXBlID0gImRvdHRlZCIsIHNpemUgPSA3KSkKYGBgCgoKU2kgcXVpZXJlcyB2ZXIgdG9kb3MgbGFzIGNhcmFjdGVyw61zdGljYXMgcXVlIGNvbnRyb2xhIHkgcXVlIHBvciB0YW50byBwdWVkZXMgbW9kaWZpY2FyIGNvbiBgdGhlbWUoKWAsIHVzYSBsYSBheXVkYSBkZSBsYSBmdW5jacOzbiBgdGhlbWUoKWAgbyBlamVjdXRhIGVuIFIgYGFyZ3ModGhlbWUpYC4KCgpFbiBnZW5lcmFsLCBzaSBxdWllcmVzIGNhbWJpYXIgYWxnw7puIGVsZW1lbnRvIGRlIHVuIGdncGxvdCwgaGFzIGRlIGhhY2VyIGB0aGVtZShlbGVtZW50byA9IGVsZW1lbnRfdGV4dCgpKWAuIFNpIHF1aWVyZXMgZWxpbWluYXIgcG9yIGNvbXBsZXRvIGFsZ8O6biBlbGVtZW50byBkZWwgZ3LDoWZpY28sIHBvciBlamVtcGxvIGxhcyBncmlkLWxpbmVzIGRlbCBncsOhZmljbywgaGFyw61hcyBgdGhlbWUocGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSlgLgoKCkV2aWRlbnRlbWVudGUgdG9kbyBlc3RvIGVzIGltcG9zaWJsZSBkZSBhcHJlbmRlciwgc8OzbG8gdGllbmVzIHF1ZSBzYWJlciBxdWUgY3VhbHF1aWVyIGVsZW1lbnRvIGRlbCBncsOhZmljbyBzZSBwdWVkZSBjYW1iaWFyIHkgdGllbmVzIHF1ZSBzYWJlciBidXNjYXIgZSBpbnRlcnByZXRhciBsYSBheXVkYS4KCgpTZSBtdXkgcG9jbyBkZSAqKmVzY2FsYXMgZGUgY29sb3IqKiwgcGVybyBzaSBxdWllcmVzIHZlciBsb3MgNjU3IGNvbG9yZXMgcXVlIHRpZW5lbiB1biBub21icmUgZW4gUiwgZWplY3V0YSBsbyBzaWd1aWVudGU6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQphYSA8LSBhcy5kYXRhLmZyYW1lKGNvbG91cnMoKSkKYGBgCgpUYW1iacOpbiBlcyBpbnRlcmVzYW50ZSBbZXN0ZSBwYXF1ZXRlXShodHRwczovL2dpdGh1Yi5jb20vRW1pbEh2aXRmZWxkdC9wYWxldHRlZXIpIHF1ZSBhZ3J1cGEgdW4gY29uanVudG8gYW1wbGlvIGRlIHBhbGV0YXMgZGUgY29sb3JlcyBwYXJhIHVzYXIgZW4gUi4gCgo8YnI+CgpQb3Igw7psdGltbywgbm8gc8OpIHNpIGNvbm9jw6lpcyBlbCBbd2ViY29taWMgWEtDRF0oaHR0cHM6Ly94a2NkLmNvbS8pLiBQdWVzIGVuIFIgdGFtYmnDqW4gaGF5IHVuIHBhcXVldGUgeSB1biB0aGVtZSBwYXJhIGhhY2VyIGdyw6FmaWNvcyBhbCBlc3RpbG8gWEtDRC4gRXMgZWwgW3BhcXVldGUgeGtjZF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3hrY2QvaW5kZXguaHRtbCkgY3V5byBhdXRvciBlcyBFbWlsaW8gVG9ycmVzLU1hbnphbmVyYSBkZSBsYSBVbml2ZXJzaWRhZCBkZSBPdmllZG8uIFtBcXXDrV0oaHR0cDovL3hrY2Quci1mb3JnZS5yLXByb2plY3Qub3JnLykgcG9kw6lpcyB2ZXIgYWxndW5vcyBncsOhZmljb3MgaGVjaG9zIGNvbiBlc3RlIGVzdGlsbyBlbiBSLiAKCkludGVudMOpIGhhY2VyIHVuIGdyw6FmaWNvIGNvbiBzdSB0aGVtZSwgcGVybyBkZXNhZm9ydHVuYWRhbWVudGUgbm8gbWUgc2FsacOzOyBwZXJvIGp1c3RvIGFsIGTDrWEgc2lndWllbnRlIHZpIFtlc3RlIHR3ZWV0XShodHRwczovL3R3aXR0ZXIuY29tL19HaWxfSGVucmlxdWVzL3N0YXR1cy8xMTY2NDQwMjYyNzczNTY3NDg4P3M9MDkpIHF1ZSBoYWNlIGFsZ28gcGFyZWNpZG8gY29uIGRhdG9zIGRlIGxvcyBTaW1wc29ucyB5IFtzdSBjw7NkaWdvXShodHRwczovL2dpdGh1Yi5jb20vR2lsSGVucmlxdWVzL1RpZHlUdWVzZGF5cy9ibG9iL21hc3Rlci8yMDE5LTA4LTI3JTIwU2ltcHNvbnMlMjBndWVzdCUyMHN0YXJzL3NpbXBzb25zLlIpIHPDrSBtZSBoYSBmdW5jaW9uYWRvLCBhZGVtw6FzIHNpbXVsYS9jb25zdHJ1eWUgZWwgZXN0aWxvIFhLQ0QgZGVzZGUgY2Vyby4gCgoKPGJsb2NrcXVvdGUgY2xhc3M9InR3aXR0ZXItdHdlZXQiPjxwIGxhbmc9ImVuIiBkaXI9Imx0ciI+8J+TiCBJIGxlYXJuZWQgYSBsb3QgZG9pbmcgdGhpcyB3ZWVrJiMzOTtzIDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vaGFzaHRhZy9UaWR5VHVlc2RheT9zcmM9aGFzaCZhbXA7cmVmX3NyYz10d3NyYyU1RXRmdyI+I1RpZHlUdWVzZGF5PC9hPi4gSSBoYWQgdG8gbWFrZSB0aGUgYXhlcyBhbmQgdGhlIGxpbmVzIGNvbm5lY3RpbmcgdGhlIHBvaW50cyBsb29rIGhhbmQtZHJhd24gKGl0IHRha2VzIHNvbWUgaml0dGVyaW5nISksIGFuZCBsZWFybiBob3cgdG8gcHV0IGEgZmlndXJlIGluIHRoZSBiYWNrZ3JvdW5kLiBIYXBweSB3aXRoIHRoZSByZXN1bHQhIENvZGUgYmVsb3cuPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9oYXNodGFnL2RhdGF2aXo/c3JjPWhhc2gmYW1wO3JlZl9zcmM9dHdzcmMlNUV0ZnciPiNkYXRhdml6PC9hPiA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL2hhc2h0YWcvcnN0YXRzP3NyYz1oYXNoJmFtcDtyZWZfc3JjPXR3c3JjJTVFdGZ3Ij4jcnN0YXRzPC9hPiA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL2hhc2h0YWcvZ2dwbG90Mj9zcmM9aGFzaCZhbXA7cmVmX3NyYz10d3NyYyU1RXRmdyI+I2dncGxvdDI8L2E+IDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vUjREU2NvbW11bml0eT9yZWZfc3JjPXR3c3JjJTVFdGZ3Ij5AUjREU2NvbW11bml0eTwvYT4gPGEgaHJlZj0iaHR0cHM6Ly90LmNvL0dyMHczN3BidDkiPnBpYy50d2l0dGVyLmNvbS9HcjB3MzdwYnQ5PC9hPjwvcD4mbWRhc2g7IEdpbCBIZW5yaXF1ZXMg8J+MuSAoQF9HaWxfSGVucmlxdWVzKSA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL19HaWxfSGVucmlxdWVzL3N0YXR1cy8xMTY2MzczODQ0MDQwMzM1MzYwP3JlZl9zcmM9dHdzcmMlNUV0ZnciPkF1Z3VzdCAyNywgMjAxOTwvYT48L2Jsb2NrcXVvdGU+IDxzY3JpcHQgYXN5bmMgc3JjPSJodHRwczovL3BsYXRmb3JtLnR3aXR0ZXIuY29tL3dpZGdldHMuanMiIGNoYXJzZXQ9InV0Zi04Ij48L3NjcmlwdD4gCgoKCkFkZW3DoXMsIGRlc3B1w6lzIHZpIHF1ZSBFdmFuZ2VseW5lIFJlaW5vbGRzIGhpem8gW2VzdGEgbWFyYXZpbGxhXShodHRwczovL2V2YW1hZXJleS5naXRodWIuaW8vdGlkeXR1ZXNkYXlfd2Fsa190aHJvdWdoL3NpbXBzb25zLmh0bWwjMSkuIExvIGhhcmVtb3MgZW4gY2xhc2U/ISBFbiBbZXN0ZSBwb3N0XShodHRwczovL2Jsb2cucmV2b2x1dGlvbmFuYWx5dGljcy5jb20vMjAxOC8wOS9jdXJ2ZS1maXR0aW5nLmh0bWwpIHB1ZWRlcyBlbmNvbnRyYXIgZWwgY8OzZGlnbyBwYXJhIHJlcHJvZHVjaXIgdW5hIGRlIGxhcyBoaXN0b3JpYXMgbyB2acOxZXRhcyBkZSBYS0NELgogCgo8YnI+CgoKIyMjIDMuMyBTbWFsbCBtdWx0aXBsZXMgbyBGYWNldHRpbmcKCgpFbCBzaXN0ZW1hIGdyw6FmaWNvIGRlIGBnZ3Bsb3QyYCBpbmNvcnBvcmEgdW5hIHTDqWNuaWNhIGVzcGVjaWFsIGxsYW1hZGEgImZhY2V0aW5nIiBxdWUgcGVybWl0ZSBkaXZpZGlyIHVuIGdyw6FmaWNvIGVuIG3Dumx0aXBsZXMgZ3LDoWZpY29zLiBDYWRhIHVubyBkZSBlc29zIG3Dumx0aXBsZXMgZ3LDoWZpY29zIHNlIHJlYWxpemEgc8OzbG8gcGFyYSBsYXMgb2JzZXJ2YWNpb25lcyBkZSB1bmEgZGUgbG9zIHZhbG9yZXMgZGUgdW5hIHZhcmlhYmxlIGNhdGVnw7NyaWNhIChvIGZhY3RvcikgaW5jbHVpZG8gZW4gZWwgY29uanVudG8gZGUgZGF0b3MuIEVzIG3DoXMgZsOhY2lsIGhhY2VybG8gcXVlIGV4cGxpY2FybG8vZXNjcmliaXJsby4KClBvciBlamVtcGxvLCBlbiBgaXJpc2AgdGVuZW1vcyBsYSB2YXJpYWJsZSBgU3BlY2llc2AsIHF1ZSBlcyBjYXRlZ8OzcmljYS4gTG8gcXVlIHNlIGhhY2UgY29uIGVsICJmYWNldHRpbmciIGVzIGRpdmlkaXIgZWwgZGF0YXNldCBlbiBncnVwb3MgeSBoYWNlciBlbCBtaXNtbyBncsOhZmljbyBwYXJhIGNhZGEgdW5vIGRlIGxvcyBncnVwb3MuIExvcyBncnVwb3Mgc2UgdmFuIGEgZGVmaW5pciBlbiBmdW5jacOzbiBkZSBsb3MgdmFsb3JlcyBkZSBsYSB2YXJpYWJsZSBTcGVjaWVzLiBSZWN1ZXJkYSBxdWUgaGF5IHRyZXMgdGlwb3MgbyBlc3BlY2llcyBkZSBsaXJpb3MuCgpQYXJhIGhhY2VyIHVuICJmYWNldHRpbmcgZ3JhcGgiIHBvZGVtb3MgdXNhciBsYXMgZnVuY2lvbmVzIGBmYWNldF93cmFwKClgIHkgYGZhY2V0X2dyaWQoKWAuIAoKUG9yIGVqZW1wbG8sIGNvbiBsYSBmdW5jacOzbiBgZmFjZXRfZ3JpZCgpYCBwdWVkZXMgZWxlZ2lyIGVudHJlIGhhY2VyIGxvcyBzbWFsbCBtdWx0aXBsZXMgcG9yIGZpbGFzIG8gcG9yIGNvbHVtbmFzLiBFbXBlY2Vtb3MgaGFjaWVuZG8gdW4gZmFjZXR0aW5nICoqcG9yIGNvbHVtbmFzKiouIEFkZW3DoXMsIGNvbiBgZmFjZXRfZ3JpZCgpYCBzZSBwdWVkZW4gdXNhciB2YXJpYXMgc2ludGF4aXMsIHBlcm8gbGEgcXVlIGFwYXJlY2UgZW4gbGEgW2NoZWF0c2hlZXQgYWN0dWFsIGRlIGdncGxvdDJdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Jlc291cmNlcy9jaGVhdHNoZWV0cy8pIHksIHBvciB0YW50bywgbGEgcmVjb21lbmRhZGEgZXMgbGFzIHF1ZSB2ZXMgZW4gbGEgc2VndW5kYSBsaW5lYTogCgoKYGBge3IsIG91dC53aWR0aCA9ICIxMDAlIiwgZmlnLmFzcCA9IDQvOX0KI3AgKyBmYWNldF9ncmlkKCAuIH4gU3BlY2llcykgICAgICAgICAgICAgICAgIyBvbGQgc2ludGF4aXMKcCArIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoU3BlY2llcykpICAgICAgICAgIyBncsOhZmljb3MgeCBjb2x1bW5hcywgc2VwYXJhbmRvIHBvciB2YWxvcmVzIGRlICdTcGVjaWVzJwpgYGAKCgpBaG9yYSBwb3IgZmlsYXM6CgoKYGBge3IsIG91dC53aWR0aCA9ICI1MCUiLCBmaWcuYXNwID0gOS85fQpwICsgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhTcGVjaWVzKSkgICAgICAgICAjIGdyw6FmaWNvcyB4IGZpbGFzCmBgYAoKClRhbWJpw6luIHBvZGVtb3MgdXRpbGl6YXIgbGEgZnVuY2nDs24gKipgZmFjZXRfd3JhcCgpYCoqLiBFc3RhIGZ1bmNpw7NuIHJlcGFydGUgbG9zIHNtYWxsIG11bHRpcGxlcyBlbiB1bmEgcmVqaWxsYSBjb24gZm9ybWEgZGUgbWF0cml6LiAKCmBgYHtyLCBvdXQud2lkdGggPSAiOTAlIiwgZmlnLmFzcCA9IDQvOX0KcCArIGZhY2V0X3dyYXAodmFycyhTcGVjaWVzKSwgbnJvdyA9IDIsIG5jb2wgPSAyKSAgICAgICAgIyBncmFmIHggZmlsYXMgeSBjb2x1bW5hcwpgYGAKCgpTaSBlbiBlbCBkYXRhc2V0IGh1Ymllc2VuIGRvcyB2YXJpYWJsZXMgY2F0ZWfDs3JpY2FzIHBvZHLDrWFtb3MgaGFjZXIgcXVlIHVuYSBkZSBlbGxhcyBzaXJ2aWVzZSBwYXJhIGxsZW5hciBsYXMgZmlsYXMgeSBsYSBvdHJhIGxhcyBjb2x1bW5hcy4gCgoKQ29tbyBgaXJpc2Agc8OzbG8gdGllbmUgdW5hIHZhcmlhYmxlIGNhdGVnw7NyaWNhIChTcGVjaWVzKSB2YW1vcyBhIGRpc2NyZXRpemFyIHVuYSBkZSBsYXMgdmFyaWFibGVzIGNvbnRpbnVhcy4gUG9yIGVqZW1wbG8gbGEgYW5jaHVyYSBkZWwgcMOpdGFsbywgY3JlYXJlbW9zIHVuYSBudWV2YSB2YXJpYWJsZSBkaXZpZGllbmRvIGxhcyBvYnNlcnZhY2lvbmVzIGRlIGBQZXRhbC5XaWR0aGAgZW4gMiBjYXRlZ29yw61hcywgcG9yIGVuY2ltYSB5IHBvciBkZWJham8gZGUgbGEgbWVkaWEgZGUgc3UgbWVkaWEuIEFkZW3DoXMgbG8gdmFtb3MgIGEgaGFjZXIgY29uIFItYmFzZSB5IGNvbiBgZHBseXI6Om50aWxlKClgLiBTZWd1cm8gcXVlIGhheSBtZWpvcmVzIGZvcm1hcywgW3BvciBlamVtcGxvXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvYXJ1bGVzL3ZlcnNpb25zLzEuNi0zL3RvcGljcy9kaXNjcmV0aXplKQoKCkNvbiBgZHBseXI6Om50aWxlKClgCgpgYGB7cn0KaXJpcyA8LSBpcmlzICU+JSBtdXRhdGUobmV3X3ZhcmlhYmxlID0gbnRpbGUoUGV0YWwuV2lkdGgsIDIpKSAKYGBgCgpDb24gUi1iYXNlIHkgbGEgZnVuY2nDs24gYGN1dCgpYDoKCmBgYHtyfQppcmlzIDwtIGlyaXMKaXJpcyRuZXdfdmFyaWFibGUgPC0gY3V0KGlyaXMkUGV0YWwuV2lkdGgsIAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygtSW5mLCBtZWFuKGlyaXMkUGV0YWwuV2lkdGgpLCBJbmYpLCAKICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoImRlYmFqby1tZWRpYSIsICJhcnJpYmEtbWVkaWEiKSkKYGBgCgoKCkFob3JhIHlhIHRlbmVtb3MgZG9zIHZhcmlhYmxlIGRpc2NyZXRhIHkgcG9kZW1vcyBoYWNlciBxdWUgYGZhY2V0X2dyaWQoKWAgdXRpbGljZSB1bmEgdmFyaWFibGUgcGFyYSBsbGVuYXIgZmlsYXMgeSBvdHJhIHBhcmEgY29sdW1uYXMuIFNlIHB1ZWRlIGVzcGVjaWZpY2FyIGRlIGRvcyBtYW5lcmFzCgoKYGBge3IsIG91dC53aWR0aCA9ICI5MCUiLCBmaWcuYXNwID0gNC85LCBldmFsID0gRkFMU0V9CmdncGxvdChpcmlzKSArIGdlb21fcG9pbnQoIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKwpmYWNldF9ncmlkKHJvd3MgPSB2YXJzKG5ld192YXJpYWJsZSksIGNvbHMgPSB2YXJzKFNwZWNpZXMpKSAgICAgICAgIyBncmFmIHggZmlsYXMgeSBjb2x1bW5hcwpgYGAKCgpgYGB7ciwgb3V0LndpZHRoID0gIjkwJSIsIGZpZy5hc3AgPSA0Lzl9CmdncGxvdChpcmlzKSArIGdlb21fcG9pbnQoIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKwpmYWNldF9ncmlkKG5ld192YXJpYWJsZSB+IFNwZWNpZXMpICAgICAKYGBgCgoKQ29tbyB2ZW1vcywgbG9zIGxpcmlvcyBkZSBsYSBjbGFzZSBzZXRvc2Egc2llbXByZSB0aWVuZSBlbCBhbmNobyBkZSBzdSBww6l0YWxvIHBvciBkZWJham8gZGUgbGEgbWVkaWEsIHkgbG9zIHZpcmdpbmljYSBzaWVtcHJlIGVzdMOhbiBwb3IgZW5jaW1hIGRlIGxhIG1lZGlhLgoKCiMjIyMgRWplcyBkZSBsb3Mgc21hbGwgbXVsdGlwbGVzCgoKUG9kZW1vcyBhanVzdGFyIGxhcyBlc2NhbGFzIGRlIGxvcyBlamVzIHBhcmEgcXVlIHNlYW4gY29tdW5lcyBwYXJhIGNhZGEgc21hbGwgbXVsdGlwbGUgKGxhIG9wY2nDs24gcG9yIGRlZmVjdG8pIG8gZGVqYXIgcXVlIGxhcyBlc2NhbGFzIGRlIGNhZGEgZ3LDoWZpY28gdmFyw61lbiBlbiBmdW5jacOzbiBkZWwgcmFuZ28gZGUgbG9zIGRhdG9zIHJlcHJlc2VudGFkb3M6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwICsgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhTcGVjaWVzKSkgICAgIy0gZXNjYWxhcyBjb211bmVzCnAgKyBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKFNwZWNpZXMpLCBzY2FsZXMgPSAiZnJlZSIpICAgIy0gbGFzIGVzY2FsYXMgZGUgY2FkYSBzbWFsbCBwdWVkZW4gdmFyaWFyCnAgKyBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKFNwZWNpZXMpLCBzY2FsZXMgPSAiZnJlZV95IikgIy0gc29sbyBkZWphbW9zIGxpYnJlL3ZhcmlhciBsYSBlc2NhbGEgZGVsIGVqZSB5CmBgYAoKClNvbG8gbXVlc3RybyBlbCByZXN1bHRhZG8gZGUgbGEgc2VndW5kYSBleHByZXNpw7NuOgoKCmBgYHtyLCBvdXQud2lkdGggPSAiOTAlIiwgZmlnLmFzcCA9IDQvOSwgZWNobyA9IEZBTFNFfQpwICsgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhTcGVjaWVzKSwgc2NhbGVzID0gImZyZWUiKSAgICMtIGxhcyBlc2NhbGFzIGRlIGNhZGEgc21hbGwgcHVlZGVuIHZhcmlhcgpgYGAKCgoqKlVuIHRydXF1aXRvOioqIGVsIGFyZ3VtZW50byBgbWFyZ2luYCBlbiBgZ2dwbG90Mjo6ZmFjZXRfZ3JpZCgpYCBhw7FhZGUgbWFyZ2VuZXMgYSBsb3Mgc21hbGwgbXVsdGlwbGVzIGZhY2lsaXRhbmRvIGxhIHZpc3VhbGl6YWNpw7NuLiBBZGVtw6FzIGHDsWFkZSB1biBudWV2byBzbWFsbCBjb24gdG9kYXMgbGFzIG9ic2VydmFjaW9uZXMuCgo8YmxvY2txdW90ZSBjbGFzcz0idHdpdHRlci10d2VldCI+PHAgbGFuZz0iZW4iIGRpcj0ibHRyIj5USUwgdGhlIGBtYXJnaW5gIGFyZ3VtZW50IGluIDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vaGFzaHRhZy9nZ3Bsb3QyP3NyYz1oYXNoJmFtcDtyZWZfc3JjPXR3c3JjJTVFdGZ3Ij4jZ2dwbG90MjwvYT4gYGZhY2V0X2dyaWQoKWAgZm9yIGFkZGluZyBtYXJnaW5zIHRvIHlvdXIgcGxvdHMuIEl0IG1ha2VzIGNvbXBhcmlzb24gZWFzaWVyIGFjcm9zcyBwYW5lbHMuIDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vaGFzaHRhZy9yc3RhdHM/c3JjPWhhc2gmYW1wO3JlZl9zcmM9dHdzcmMlNUV0ZnciPiNyc3RhdHM8L2E+IDxhIGhyZWY9Imh0dHBzOi8vdC5jby9WdjB4Y2xzcXNUIj5waWMudHdpdHRlci5jb20vVnYweGNsc3FzVDwvYT48L3A+Jm1kYXNoOyBXZSBhcmUgUi1MYWRpZXMgKEBXZUFyZVJMYWRpZXMpIDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vV2VBcmVSTGFkaWVzL3N0YXR1cy8xMTIzMTA5MDk0NDIyOTIxMjE2P3JlZl9zcmM9dHdzcmMlNUV0ZnciPkFwcmlsIDMwLCAyMDE5PC9hPjwvYmxvY2txdW90ZT4gPHNjcmlwdCBhc3luYyBzcmM9Imh0dHBzOi8vcGxhdGZvcm0udHdpdHRlci5jb20vd2lkZ2V0cy5qcyIgY2hhcnNldD0idXRmLTgiPjwvc2NyaXB0PiAKClB1ZWRlcyBwcm9iYXJsbyB0w7ogbWlzbW8gY29ycmllbmRvIGxvIHNpZ3VpZW50ZToKCmBgYHtyLCBvdXQud2lkdGggPSAiOTAlIiwgZmlnLmFzcCA9IDQvOX0KcCArIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoU3BlY2llcyksICBtYXJnaW5zID0gVFJVRSkgICAKYGBgCgoKT3RybyB0cnVjbywgZXN0YSB2ZXogYXZhbnphZG86IFVuIFtnaXN0XShodHRwczovL2dpc3QuZ2l0aHViLmNvbS9wYWRwYWRwYWRwYWQvZGMxZjQ1MjBlNGY5NTMwYjJjNzBmZWQwZTRhNDI1ZTkpIHBhcmEgcG9uZXIgbGFiZWxzIGEgbG9zIHNtYWxscyBtdWx0aXBsZXMuCgo8YnI+CgoKIyMjIDMuNCBBbm90YWNpb25lcwoKPiBBbm5vdGF0aW9ucyBhcmUgYSBzcGVjaWFsIHR5cGUgb2YgbGF5ZXIgdGhhdCBkb27igJl0IGluaGVyaXQgZ2xvYmFsIHNldHRpbmdzIGZyb20gdGhlIHBsb3QuIFRoZXkgYXJlIHVzZWQgdG8gYWRkIGZpeGVkIHJlZmVyZW5jZSBkYXRhIHRvIHBsb3RzLgoKTGFzIGFub3RhY2lvbmVzIGVuIGxvcyBncsOhZmljb3MgcGVybWl0ZW4gcmVzYWx0YXIgYWxnw7puIGZlbsOzbWVub3MgbyBvYnNlcnZhY2nDs25lcyBkZSBpbnRlcsOpcyB5IHNvbiBpbXBvcnRhbnRlcyBhIGxhIGhvcmEgZGUgY29udGFyIGhpc3RvcmlhcyAoc3Rvcnl0ZWxsaW5nKSBjb24gbG9zIGdyw6FmaWNvcyB5IHZpc3VhbGl6YWNpb25lcy4gCgpFbiBlbCBlbnRvcm5vIGBnZ3Bsb3RgIHBvZGVtb3MgaGFjZXIgYW5vdGFjaW9uZXMgZW4gbnVlc3RybyBncsOhZmljb3MgZGUgdmFyaWFzIG1hbmVyYXMsIHBvciBlamVtcGxvIGNvbiBgYW5ub3RhdGUoKWAuIEF1bnF1ZSBjb25jZXB0dWFsbWVudGUsIGNvbW8gc2XDsWFsYSBIYWRsZXksIGxhcyBhbm90YWNpb25lcyBzb24gbWV0YWRhdG9zLCBkZXNkZSBlbCBwdW50byBkZSB2aXN0YSBwcsOhY3RpY28gc2UgdXNhbiBsb3MgbWlzbWFzIGZ1bmNpb25lcyBvIGdlb21zIHBhcmEgbWFuaXB1bGFybG9zLiAgCgoKVGFtYmnDqW4gZXhpc3RlbiBhbGd1bmFzIGZ1bmNpb25lcyBhdXhpbGlhcmVzIGVuIGdncGxvdDIgeSBlbiBwYXF1ZXRlcyBlc3BlY8OtZmljb3MgcGFyYSBoYWNlciBhbm90YWNpb25lcyBlbiBncsOhZmljb3MgZ2dwbG90LiBQb3IgZWplbXBsbywgY3VhbmRvIHNlIGhhY2VuIGFub3RhY2lvbmVzIGVuIHVuIGdyw6FmaWNvIGRlIHB1bnRvcyBlcyBmw6FjaWwgcXVlIGxhcyBhbm90YWNpb25lcyBjYWlnYW4gdW5hcyBlbmNpbWEgZGUgb3RyYXMsIGVsIHBhcXVldGUgW2dncmVwZWxdKGh0dHBzOi8vYnVmZi5seS8ySFhFcnhMKSBwZXJtaXRlIGFsaXZpYXIgZXN0ZSBwcm9ibGVtYS4KClV0aWxpY2Vtb3MgbGEgZnVuY2nDs24gYGFubm90YXRlKClgLiBQb3IgZWplbXBsbywgZWwgc2lndWllbnRlIGNodW5rIGhhY2UgYWxndW5hcyBhbm90YWNpb25lcyBzaW4gbXVjaG8gc2VudGlkbyBwZXJvIGbDoWNpbGVzIGRlIGVudGVuZGVyOgoKCmBgYHtyLCBlY2hvID0gRkFMU0V9CnAgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKyBnZW9tX3BvaW50KCkKYGBgCgpQb3IgZWplbXBsbywgZWwgc2lndWllbnRlIGNodW5rIHVzYSBgYW5ub3RhdGUoKWAgcGFyYSBoYWNlciBhbGd1bmFzIGFub3RhY2lvbmVzIHNpbiBtdWNobyBzZW50aWRvLCBwZXJvIGbDoWNpbGVzIGRlIGVudGVuZGVyOgoKYGBge3J9CnAgKyBhbm5vdGF0ZShnZW9tID0gInRleHQiLCB4ID0gNiwgeSA9IDIsIGxhYmVsID0gIlVuYSBhbm90YWNpw7NuIiwgc2l6ZSA9IDUpICsKICAgIGFubm90YXRlKCJyZWN0IiwgeG1pbiA9IDYsIHhtYXggPSA3LHltaW4gPSAtSW5mLCB5bWF4ID0gSW5mLCBhbHBoYSA9IDAuMiwgZmlsbCA9ICJwaW5rIikgKyAKICAgIGFubm90YXRlKCJzZWdtZW50IiwgeCA9IDUsIHhlbmQgPSA3LCB5ID0gNiwgeWVuZCA9IDgsIGNvbG91ciA9ICJibHVlIikgCmBgYAoKPGJyPgoKIyMjIyBBbm90YWNpb25lcyBkZSB0ZXh0byBlbiBsYXMgb2JzZXJ2YWNpb25lcwoKQWdyZWdhciB0ZXh0byBhIHVuIGdyw6FmaWNvIGVzIHVuYSBkZSBsYXMgZm9ybWFzIG3DoXMgY29tdW5lcyBkZSBhbm90YWNpw7NuLiBQb3IgZWplbXBsbywgcGFyYSBzZcOxYWxpemFyIGUgaWRlbnRpZmljYXIgb2JzZXJ2YWNpb25lcyBhbsOzbWFsYXMuIFNpbiBlbWJhcmdvLCBjb21vIHNlw7FhbGEgSGFkbGV5LCBhw7FhZGlyIHRleHRvIG5vIGVzIGbDoWNpbCBwb3IgbGEgZm9ybWEgZW4gbGEgcXVlIFIgbWFuZWphIGxhcyBmdWVudGVzLgoKTGEgZnVuY2nDs24gcHJpbmNpcGFsIHBhcmEgZWwgZXRpcXVldGFkbyBkZSBncsOhZmljb3MgZXMgYGdlb21fdGV4dCgpYC4gUG9yIGVqZW1wbG86CgoKYGBge3IsIGV2YWwgPSBUUlVFLCBvdXQud2lkdGggPSAiNjAlIn0KcCArIGdlb21fdGV4dChhZXMobGFiZWwgPSBTcGVjaWVzKSkKYGBgCgpUYW1iacOpbiBwb2TDrWFtb3MgaGFiZXIgYcOxYWRpZG8gZWwgdmFsb3IgZGUgbGEgbG9uZ2l0dWQgZGVsIHDDqXRhbG8uIAoKCmBgYHtyLCBldmFsID0gVFJVRSwgb3V0LndpZHRoID0gIjYwJSJ9CnAgKyBnZW9tX3RleHQoYWVzKGxhYmVsID0gUGV0YWwuTGVuZ3RoKSkKYGBgCgoKCkhlbW9zIGHDsWFkaWRvIGEgY2FkYSBvYnNlcnZhY2nDs24gdW4gdGV4dG8sIHByb3ZlbmllbnRlIGRlIGFsZ3VuYSBkZSBsYXMgdmFyaWFibGVzIGRlIGBpcmlzYC4gRXN0ZSBncsOhZmljbyBubyBlcyBtdXkgw7p0aWwsIHBlcm8gbGEgdMOpY25pY2Egc8OtLiBJbWFnaW5hIHF1ZSBxdWVyZW1vcyBtYXJjYXIgbG9zIGxpcmlvcyA0NSB5IDE0MF5bTyBtYXJjYXIvYW5vdGFyIGxvcyBsaXJpb3MgMiBsaXJpb3MgbcOhcyBncmFuZGVzIG8gZWwgbGlyaW8gbWVkaWFuby4gwr9MbyBoYWPDqWlzP10uIFBvZGVtb3MgaGFjZXIgbG8gc2lndWllbnRlOgoKCmBgYHtyLCBldmFsID0gVFJVRSwgb3V0LndpZHRoID0gIjYwJSJ9CmlyaXNfeCA8LSBpcmlzW2MoNDUsIDE0MCksXSAjLSBzZWxlY2Npb25hbW9zIGVsIGxpcmlvIDQ1IHkgZWwgMTQwIChjb24gUi1iYXNlISEpCnAgKyBnZW9tX3RleHQoZGF0YSA9IGlyaXNfeCwgYWVzKGxhYmVsID0gU3BlY2llcyksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDUpCmBgYAoKUG9kZW1vcyBhanVzdGFyIGxhIHBvc2ljacOzbiB5IHRhbWHDsW8gZGVsIHRleHRvLCBldGMuLiBQb3IgZWplbXBsbywgcG9kZW1vcyBjYW1iaWFyIGxhIGFsaW5lYWNpw7NuIGRlIGxhcyBhbm90YWNpb25lcyBjb24gY29uIGBoanVzdCjigJxsZWZ04oCdLCDigJxjZW50ZXLigJ0sIOKAnHJpZ2h04oCdLCDigJxpbndhcmTigJ0sIOKAnG91dHdhcmTigJ0pYCB5IGB2anVzdCAo4oCcYm90dG9t4oCdLCDigJxtaWRkbGXigJ0sIOKAnHRvcOKAnSwg4oCcaW53YXJk4oCdLCDigJxvdXR3YXJk4oCdKWAuCgo8YnI+CgoKIyMjIyBMaW5lYXMKClBvZGVtb3MgYcOxYWRpciBsaW5lYXM6IAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcCArIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDYpCnAgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA1LCBzaXplID0gMS43LCBjb2xvdXIgPSAiYmxhY2siLCBsaW5ldHlwZSA9ICJkYXNoZWQiKQpwICsgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMC43LCBzbG9wZSA9IDAuNCwgc2l6ZSA9IDIpCmBgYAoKPGJyPgoKCiMjIyAzLjUgQ2FtYmlhbmRvIGxvcyBsw61taXRlcyBkZSBsb3MgZWplcwoKClNpIHF1aWVyZXMgbW9kaWZpY2FyIGVsIHJlY29ycmlkbyBkZSBsb3MgZWplcywgbG9zICJsw61taXRlcyIgZGUgbG9zIGVqZXMsIHB1ZWRlcyB1c2FyIGBsaW1zKClgLiBQYXJhIGxvcyBlamVzIFggZSBZIGhheSBkb3MgZnVuY2lvbmVzIGF1eGlsaWFyZXM6IGB4bGltKClgIGUgYHlsaW0oKWAuIAoKCmBgYHtyLCBvdXQud2lkdGggPSAiNjAlIn0KcCArIGxpbXMoY29sb3IgPSBjKCJzZXRvc2EiKSwgeCA9IGMoTkEsNiksIHkgPSBjKDEsOCkpCmBgYAoKCgpgYGB7ciwgb3V0LndpZHRoID0gIjYwJSJ9CnAgKyB4bGltKGMgKDQsIDYpKSArIHlsaW0oYyhOQSwgNSkpIApgYGAKClNlIHB1ZWRlIGhhc3RhIGRhciBsYSB2dWVsdGEgYSBsb3MgZWplcwoKCmBgYHtyLCBvdXQud2lkdGggPSAiNjAlIn0KcCArIHhsaW0oYyAoNywgMykpICsgeWxpbShjKE5BLCA1KSkgCmBgYAoKCkxvcyBsaW1pdGVzIG8gZG9taW5pbyBkZWwgZ3LDoWZpY28gc3VlbGVuIG9idGVuZXJzZSBhdXRvbcOhdGljYW1lbnRlIGRlIGxvcyBkYXRvcywgcGVybywgb3RyYSB2ZXogYWNjb3JkaW5nIHRvIEhhZGxleSwgaGF5IGRvcyByYXpvbmVzIHBvciBsYXMgcXVlIHBvZGVtb3MgZXN0YXIgaW50ZXJlc2Fkb3MgZW4gY2FtYmlhciBsb3MgbMOtbWl0ZXMgZGVsIGdyw6FmaWNvOiAKCiAgMSkgY2VudHJhcm5vcyBlbiB1bmEgcmVnacOzbiBlc3BlY2lmaWNhIGRlbCBncsOhZmljbyAKICAKICAyKSBhdW1lbnRhciBsb3MgbMOtbWl0ZXMgcGFyYSBxdWUgdmFyaW9zIGdyw6FmaWNvcyBhanVzdGVuIHN1cyBlc2NhbGFzLiAgIAoKPGJyPgoKUG9yIGVqZW1wbG8sIHNpIGRlc3B1w6lzIGRlIGhhY2VyIHVuIGdyw6FmaWNvIHF1aWVyZXMgY2VudHJhcnRlIHPDs2xvIGVuIHVuYSBwYXJ0ZTsgZXMgZGVjaXIsIGhhY2VyIHVuIHpvb20gc29icmUgdW5hIHBhcnRlIGRlbCBncsOhZmljbywgdGVuZW1vcyAyIGFsdGVybmF0aXZhczoKCjEuIEJvcnJhciBsb3MgcHVudG9zIHF1ZSBjYWVuIGZ1ZXJhIGRlIGxvcyBsaW1pdGVzIGRlIGxvIHF1ZSBxdWllcmFzIHF1ZSBzZSB2aXN1YWxpY2UgKHNpIGVuIHVuYSBlc2NhbGEgY29udGludWEgc29sbyBxdWllcmVzIHVzYXIgdW4gbMOtbWl0ZSBwb24gTkEpOgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CnAgKyB4bGltKGMoNCwgNSkpICsgeWxpbShjKE5BLCA1KSkgIy0gY3VpZGFkbywgc2UgcHVlZGVuIGJvcnJhciBvYnNlcnZhY2lvbmVzCmBgYAoKQ29uIGVzdGUgZW5mb3F1ZSB0aWVuZXMgcXVlIHRlbmVyIGN1aWRhZG8sIHlhIHF1ZSAgc2kgcG9yIGVqZW1wbG8gZGVzcHXDqXMgdXRpbGl6YXIgYWxndW5hIHRyYW5zZm9ybWFjacOzbiBlc3RhZMOtc3RpY2EgY29tbyBwb3IgZWplbXBsbyBgZ2VvbV9zbW9vdGgoKWAsIGxhcyBvYnNlcnZhY2lvbmVzIGVsaW1pbmFkYXMgYWwgYWp1c3RhciBsb3MgbMOtbWl0ZXMgbm8gZW50cmFyw6FuIGVuIGVsIGPDoWxjdWxvIGVzdGFkw61zdGljby4gCgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CnAgKyBnZW9tX3Ntb290aChjb2xvciA9ICJwdXJwbGUiKQpwICsgZ2VvbV9zbW9vdGgoY29sb3IgPSAicHVycGxlIikgKyB4bGltKGMoNCwgNS43KSkgKyB5bGltKGMoMS41LCA1KSkgICAjIGRlbGV0ZXMgcG9pbnRzCmBgYAoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIGZpZy5hc3AgPSA1LzksIG91dC53aWR0aCA9ICI5MCUifQpwMSA8LSBwICsgZ2VvbV9zbW9vdGgoY29sb3IgPSAicHVycGxlIikKcDIgPC0gcCArIGdlb21fc21vb3RoKGNvbG9yID0gInB1cnBsZSIpICsgeGxpbShjKDQsIDUuNykpICsgeWxpbShjKDEuNSwgNSkpICAgIyBkZWxldGVzIHBvaW50cwoKCnAxICsgcDIgKyBwbG90X2xheW91dChuY29sID0gMikKYGBgCgoKMi4gQ2FtYmlhciBsb3MgbMOtbWl0ZXMgZGUgbG9zIGVqZXMgWCBlIFkgaGFjaWVuZG8gdW4gem9vbSBlbiBsYSByZWdpw7NuIGRlIGludGVyw6lzIHBlcm8gc2luIGVsaW1pbmFyIHB1bnRvcy4gRXN0byBsbyBjb25zZWd1aW1vcyBjb24gYGNvb3JkX2NhcnRlc2lhbigpYC4KCmBgYHtyLCBldmFsID0gRkFMU0V9CnAgKyBnZW9tX3Ntb290aChjb2xvciA9ICJwdXJwbGUiKQpwICsgZ2VvbV9zbW9vdGgoY29sb3IgPSAicHVycGxlIikgKyBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoNCwgNS43KSwgeWxpbSA9IGMoMS41LCA1KSkKYGBgCgoKCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBmaWcuYXNwID0gNS85LCBvdXQud2lkdGggPSAiOTAlIn0KcDEgPC0gcCArIGdlb21fc21vb3RoKGNvbG9yID0gInB1cnBsZSIpCnAyIDwtIHAgKyBnZW9tX3Ntb290aChjb2xvciA9ICJwdXJwbGUiKSArIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYyg0LCA1LjcpLCB5bGltID0gYygxLjUsIDUpKQoKCnAxICsgcDIgKyBwbG90X2xheW91dChuY29sID0gMikKYGBgCgoKPGJyPgoKCiMjIyAzLjYgRXNjYWxhcwoKPiBTY2FsZXMgY29udHJvbCB0aGUgZGV0YWlscyBvZiBob3cgZGF0YSB2YWx1ZXMgYXJlIHRyYW5zbGF0ZWQgdG8gdmlzdWFsIHByb3BlcnRpZXMuIE92ZXJyaWRlIHRoZSBkZWZhdWx0IHNjYWxlcyB0byB0d2VhayBkZXRhaWxzIGxpa2UgdGhlIGF4aXMgbGFiZWxzIG9yIGxlZ2VuZCBrZXlzLCBvciB0byB1c2UgYSBjb21wbGV0ZWx5IGRpZmZlcmVudCB0cmFuc2xhdGlvbiBmcm9tIGRhdGEgdG8gYWVzdGhldGljLiBsYWJzKCkgYW5kIGxpbXMoKSBhcmUgY29udmVuaWVudCBoZWxwZXJzIGZvciB0aGUgbW9zdCBjb21tb24gYWRqdXN0bWVudHMgdG8gdGhlIGxhYmVscyBhbmQgbGltaXRzLgoKTGFzIGVzY2FsYXMgcGVybWl0ZW4gbGVlci9pbnRlcnByZXRhciB1biBncsOhZmljbzsgcGVybWl0ZW4gaW50ZXJwcmV0YXIgbG9zIGVsZW1lbnRvcyBnZW9tw6l0cmljb3MgKHBvciBlamVtcGxvIGVuIG51ZXN0cm8gZ3LDoWZpY28sIGxvcyBwdW50b3MpIGVuIGZ1bmNpw7NuIGRlIGxvcyB2YWxvcmVzIG9yaWdpbmFsZXMgZGUgbGFzIG9ic2VydmFjaW9uZXMuIExhcyBlc2NhbGFzIHNvbiB1biBlbGVtZW50byBtw6FzIGRlIGxvcyBncsOhZmljb3MgZ2dwbG90IHkgc2UgcHJvZHVjZW4vY29udHJvbGFuIGNvbiBsYSBmYW1pbGlhIGRlIGZ1bmNpb25lcyBgc2NhbGVfeHgoKWAKCkVuIGBnZ3Bsb3QyYCBsYXMgZXNjYWxhcyBvIGd1w61hcyBzZSBwcm9kdWNlbiBhdXRvbcOhdGljYW1lbnRlLCBubyB2ZW1vcyBxdWUgaGFnYW1vcyBuYWRhLCBwZXJvIHVuZGVyIHRoZSBob29kIHNlIGVzdMOhbiBmaWphbmRvIGNvbiBsYSBmYW1pbGlhIGRlIGZ1bmNpb25lcyBgc2FsZV94eCgpYCBxdWUgc29uIGxhcyBxdWUgY29udHJvbGFuIGNvbW8gc2UgbWFwZWFuIGxvcyB2YWxvcmVzIGRlIGxhcyB2YXJpYWJsZXMgY29uIGxhcyBwcm9waWVkYWRlcyBlc3TDqXRpY2FzIGRlIG51ZXN0cm8gZ3LDoWZpY28gKHBvciBlamVtcGxvIGVsIGVqZSBYKSBkZSBmb3JtYSBxdWUgcG9kYW1vcyBpbnRlcnByZXRhciBsYSBwb3NpY2nDs24gZGUgbG9zIGRpc3RpbnRvcyBwdW50b3MgbWlyYW5kbyBsYXMgZXNjYWxhcy4gTGFzIGVzY2FsYXMgdGFtYmnDqW4gY29uc3RydXllbiBsb3MgZWxlbWVudG9zIHF1ZSBwZXJtaXRlbiBsZWVyL2ludGVycHJldGFyIGxvcyBncsOhZmljb3M6IGxvcyBlamVzIHkgbGFzIGxleWVuZGFzLgoKQ29tbyBgZ2dwbG90MmAgaGFjZSBlbCBtYXBlbyB5IGdlbmVyYSBsYXMgZXNjYWxhcyB5IGxleWVuZGFzIGF1dG9tw6F0aWNhbWVudGUsIGVuIGxhIHByw6FjdGljYSBwb2RlbW9zIGhhY2VyIGdyw6FmaWNvcyBzaW4gc2FiZXIgY29tbyBmdW5jaW9uYW4geSwgcG9yIHRhbnRvLCBzaW4gc2FiZXIgbWFuaXB1bGFyIGVzdGUgZWxlbWVudG8gZGUgdW4gZ3LDoWZpY28gZ2dwbG90LiBQZXJvIHNpIGFwcmVuZGVtb3MgYSBtYW5pcHVsYXIgbGFzIGVzY2FsYXMsIGVzdG8gbm9zIGRhcsOhIG3DoXMgZmxleGliaWxpZGFkIGEgbGEgaG9yYSBkZSB1dGlsaXphciBgZ2dwbG90MmAuCgpFbiBtdWNob3MgdGlwb3MgZGUgZGF0b3MgZXMgaW1wb3J0YW50ZSBwYXJhcnNlIGEgcGVuc2FyIGN1YWwgZXMgbGEgbWVqb3IgZXNjYWxhIHBhcmEgcmVwcmVzZW50YXIgbGFzIHZhcmlhYmxlcy4gUXVpesOhcyBzZWEgY29udmVuaWVudGUgY2FtYmlhciBsYSBlc2NhbGEgZGUgdW4gZWplIHBhcmEgZGlzdHJpYnVpciBtZWpvciBsYXMgb2JzZXJ2YWNpb25lcyBlbiBlbCBlc3BhY2lvLCBvIHBhcmEgaW50ZXJwcmV0YXIgbWVqb3IgbGFzIHZhcmlhY2lvbmVzIGVudHJlIG9ic2VydmFjaW9uZXM7IHBvciBlamVtcGxvIGxhIGVzY2FsYSBsb2dhcsOtdG1pY2EgbyBlbiBwb3JjZW50YWplcyBzb24gYSB2ZWNlcyBtw6FzIGFwcm9waWFkYXMgcXVlIGxhcyBlc2NhbGFzIG9yaWdpbmFsZXMuCgoKRW4gcmVhbGlkYWQsIHBhcmEgY2FkYSBwYXIgdmFyaWFibGUvZXN0w6l0aWNhIHJlcHJlc2VudGFkYSBlbiB1biBncsOhZmljbyBnZ3Bsb3QgZXMgbmVjZXNhcmlhIHVuYSBlc2NhbGEsIHkgdGVuZHLDrWEgcXVlIGZpamFyc2UgY29uIHVuYSBkZSBsYXMgZnVuY2lvbmVzIGRlIGxhIGZhbWlsaWEgYHNjYWxlX3h4KClgLiBSZWFsbWVudGUgY3VhbmRvIGhhY2Vtb3MgZXN0ZSBncsOhZmljbywgZW4gZWwgcXVlIGFzb2NpYW1vcyAzIHZhcmlhYmxlcyBhIDMgcHJvcGllZGFkZXMgZXN0w6l0aWNhcyBjb24gYGFlcygpYCwgc2UgbmVjZXNpdGFyw61hIGVzcGVjaWZpY2FyIGxhcyBlc2NhbGFzIGRlIGxhcyAzIHZhcmlhYmxlczoKCgpgYGB7cn0KcCA8LSBnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyAgZ2VvbV9wb2ludChhZXMgKGNvbG9yID0gU3BlY2llcykpIApgYGAKCkJpZW4sIHBlcm8gZW50b25jZXMgwr9wb3IgcXXDqSBubyBsbyBoYWNlbW9zPywgwr9wb3IgcXXDqSBubyBlc3BlY2lmaWNhbW9zIGxhcyBlc2NhbGFzPyBQdWVzIHBvcnF1ZSBsbyBoYWNlIGBnZ3Bsb3QyYCBwb3Igbm9zb3Ryb3MuIEVuIHJlYWxpZGFkIGN1YW5kbyBlamVjdXRhbW9zIGxhIGV4cHJlc2nDs24gYW50ZXJpb3IsIHJlYWxtZW50ZSBzZSBlc3TDoSBoYWNpZW5kbyBsbyBzaWd1aWVudGU6CgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgICBnZW9tX3BvaW50KGFlcyAoY29sb3IgPSBTcGVjaWVzKSkgKwogIHNjYWxlX3hfY29udGludW91cygpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKCkgKyAKICBzY2FsZV9jb2xvcl9kaXNjcmV0ZSgpCmBgYAoKRXMgZGVjaXIsIGBnZ3Bsb3QyYCBhc2lnbmEgYSBjYWRhIHZhcmlhYmxlIHVuYSBlc2NhbGEsIGEgbGFzIHZhcmlhYmxlcyBjb250aW51YXMgbGVzIGFzaWduYSB1bmEgZXNjYWxhIGNvbnRpbnVhIHkgYSBsYXMgY2F0ZWfDs3JpY2FzIHVuYSBlc2NhbGEgZGlzY3JldGEuIFBFUk8sIHNpIHF1ZXJlbW9zLCBzaSBsbyBjb25zaWRlcmFtb3MgYXByb3BpYWRvIHBvZHLDrWFtb3MgY2FtYmlhciBsYSBlc2NhbGEuIFBvciBlamVtcGxvOgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CnAgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX2NvbG91cl9ncmV5KCkKcCArIHNjYWxlX3hfc3FydCgpICsgc2NhbGVfeV9sb2cxMCgpCnAgKyBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSAibG9nIikKcCArIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikKYGBgCgpTb2xvIHJlcHJlc2VudG8gbGEgcHJpbWVyYSB0cmFuc2Zvcm1hY2nDs24sIGVuIGxhIHF1ZSBzZSBkYSBsYSB2dWVsdGEgYSBsYSBlc2NhbGEgZGVsIGVqZSBZIHkgZWwgY29sb3IsIGFzb2NpYWRvIGEgbGEgdmFyaWFibGUgU3BlY2llcywgcGFzYSBhIGVzY2FsYSBkZSBncmlzZXM6CgpgYGB7ciwgZWNobyA9IEZBTFNFLCBmaWcuYXNwID0gNS85LCBvdXQud2lkdGggPSAiNjAlIn0KcCArIHNjYWxlX3lfcmV2ZXJzZSgpICsgc2NhbGVfY29sb3VyX2dyZXkoKQpgYGAKCjxicj4KCiMjIyMgTGFiZWxzIGRlIGxvcyBlamVzCgpUYW1iacOpbiBzZSBwdWVkZW4gbW9kaWZpY2FyIGxvIHF1ZSB2ZW1vcyBlbiBsb3MgZXNjYWxhcy4gQW50ZXMgdmFtb3MgYSBtb2RpZmljYXIgbG9zIHTDrXR1bG9zIGRlIGxvcyBlamVzIHkgbGV5ZW5kYXMuCgoKYGBge3IsIGV2YWwgPSBUUlVFfQpwIDwtIHAgKyBsYWJzKHggPSAiRWplIFgiLCB5ID0gIkVqZSBZIiwgY29sb3IgPSAiTGV5ZW5kYVxuIHBhcmEgZWwgY29sb3IiKQpgYGAKCgpEZSBtb21lbnRvIGxhIGVzY2FsYSBkZWwgZWplIFggdmFyaWEgYXByb3hpbWFkYW1lbnRlIGRlIDMgYSA4LiBMb3MgdMOtdHVsb3MgZGUgbGEgZXNjYWxhIGRlbCBlamUgWCBzw7NsbyBtdWVzdHJhIGxvcyB2YWxvcmVzIDUsIDYsIDcgeSA4LiBWYW1vcyBhIG1vZGlmaWNhciAiZWwgdGV4dG8iLCBsb3MgbsO6bWVyb3MgcXVlIHNlIHZlbiB5IHF1ZSBzaXJ2ZW4gZGUgZ3XDrWEgcGFyYSBsYSBlc2NhbGEgZGVsIGVqZSBYOgoKYGBge3IsIGV2YWwgPSBUUlVFLCBvdXQud2lkdGggPSAiNDUlIn0KcCArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMywgMTAsIDAuNSksIGxpbWl0cyA9IGMoMywgMTApKSAKYGBgCgoKRW4gZWwgZWplIFkgc2UgdmVuIGxvcyBuw7ptZXJvcyAyLCA0IHkgNi4gQ2FtYmllbW9zIGxvcyBsYWJlbHMgZGUgc3UgZXNjYWxhIChubyBkZWwgZWplIFksIHNpbm8gZGUgbGEgZXNjYWxhIGRlbCBlamUgWSk6CgoKYGBge3IsIGV2YWwgPSBUUlVFLCBvdXQud2lkdGggPSAiNDUlIn0KcCArIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTIsIDAuMjUpLCAgbGFiZWwgPSBzY2FsZXM6OmRvbGxhcikgCmBgYAoKCgpUYW1iacOpbiBzZSBwdWVkZW4gbW9kaWZpY2FyIGxvcyBkZSBsYSBsZXllbmRhIHBhcmEgZWwgY29sb3I6CgoKYGBge3IsIGV2YWwgPSBUUlVFLCBvdXQud2lkdGggPSAiNDUlIn0KcCArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJwdXJwbGUiLCAicGluayIsICJyZWQyIiksIG5hbWUgPSAiRXNwZWNpZXNcbiBkZSBsaXJpb3MiKQpgYGAKClNpIGh1Ymllc2UgdXNhZG8gdW5hIHZhcmlhYmxlIGRpc2NyZXRhIGFzb2NpYWRhICBhIGxhIGVzdMOpdGljYSAic2hhcGUiLCB0ZW5kcsOtYW1vcyBxdWUgdXNhciBgc2NhbGVfc2hhcGVfZGlzY3JldGUoKWAsIHNpIGxhIHZhcmlhYmxlIGFzb2NpYWRhIGEgc2hhcGUgZnVlc2UgY29udGludWE6IGBzY2FsZV9zaGFwZV9jb250aW51b3VzKClgCgrCv1kgc2kgaHViacOpc2Vtb3MgdXNhZG8gdW5hIHZhcmlhYmxlIGNvbnRpbnVhIHBhcmEgbGEgZXN0w6l0aWNhICJmaWxsIj8gUHVlcyBgc2NhbGVfZmlsbF9jb250aW51b3VzKClgCgo8YnI+CgojIyMjIEZhbWlsaWEgZGUgZnVuY2lvbmVzIGRlIGVzY2FsYQoKICDigKIgc2NhbGVfKl9jb250aW51b3VzKCkgIAogIOKAoiBzY2FsZV8qX2Rpc2NyZXRlKCkgIAogIOKAoiBzY2FsZV8qX29yZGluYWwoKSAgCiAg4oCiIHNjYWxlXypfbWFudWFsKCkgIAogIOKAoiBzY2FsZV97Y29sb3IvZmlsbH1fYnJld2VyKCkgIAogIOKAoiBzY2FsZV97Y29sb3IvZmlsbH1fZGlzdGlsbGVyKCkgIAogIOKAoiBzY2FsZV97Y29sb3IvZmlsbH1fZ3JhZGllbnQoKSAgCgoKPGJyPgoKIyMjIDMuNyBTdGF0cyAodHJhbnNmb3JtYWNpb25lcyBlc3RhZMOtc3RpY2FzKQoKClB1ZWRlIGxlZXJzZSBlbiBsYSB3ZWIgZGUgYGdncGxvdDJgLCBjb25jcmV0YW1lbnRlIFthcXXDrV0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlLykgbG8gc2lndWllbnRlOgoKPiBBIGxheWVyIGNvbWJpbmVzIGRhdGEsIGFlc3RoZXRpYyBtYXBwaW5nLCBhIGdlb20gKGdlb21ldHJpYyBvYmplY3QpLCBhIHN0YXQgKHN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9uKSwgYW5kIGEgcG9zaXRpb24gYWRqdXN0bWVudC4gVHlwaWNhbGx5LCB5b3Ugd2lsbCBjcmVhdGUgbGF5ZXJzIHVzaW5nIGEgZ2VvbV8gZnVuY3Rpb24gCgpQRVJPCgo+IEEgaGFuZGZ1bCBvZiBsYXllcnMgYXJlIG1vcmUgZWFzaWx5IHNwZWNpZmllZCB3aXRoIGEgc3RhdF8gZnVuY3Rpb24sIGRyYXdpbmcgYXR0ZW50aW9uIHRvIHRoZSBzdGF0aXN0aWNhbCB0cmFuc2Zvcm1hdGlvbiByYXRoZXIgdGhhbiB0aGUgdmlzdWFsIGFwcGVhcmFuY2UuIFRoZSBjb21wdXRlZCB2YXJpYWJsZXMgY2FuIGJlIG1hcHBlZCB1c2luZyBzdGF0KCkuCgpBbGd1bm9zIGdyw6FmaWNvcywgY29tbyBsb3MgZ3LDoWZpY29zIGRlIHB1bnRvcywgbm8gcmVxdWllcmVuIGRlbCB1c28gZGUgdHJhbnNmb3JtYWNpb25lcyBlc3RhZMOtc3RpY2FzIGRlIGxhcyBvYnNlcnZhY2lvbmVzLCBwZXJvIG90cm9zIGdyw6FmaWNvcyBjb21vIHJlY3RhcyBvIGN1cnZhcyBkZSBwcmVkaWNjacOzbiBvIGNvbW8gbG9zIGJveHBsb3RzIG8gZGlhZ3JhbWFzIGRlIGNhamEsIHPDrSBxdWUgbGFzIG5lY2VzaXRhbiBeW1BvciBlamVtcGxvLCBwaWVuc2EgcXVlIGxvIHF1ZSBzZSB2aXN1YWxpemEgY29uIGBnZW9tX3Ntb3RoKClgIG5vIHNvbiBsb3MgZGF0b3Mgb3JpZ2luYWxlcyBzaW5vLCBwb3IgZWplbXBsbywgbGEgbGluZWEgZGUgcmVncmVzacOzbiBzaSB1c2Ftb3MgYGdlb21fc21vdGgobWV0aG9kID0gImxtIilgXS4gUG9kZW1vcyB1c2FyIHRyYW5zZm9ybWFjaW9uZXMgZXN0YWTDrXN0aWNhcyBlbiBncsOhZmljb3MgZ2dwbG90IGNvbiBsYSBmYW1pbGlhIGRlIGZ1bmNpb25lcyAqKmBzdGF0X3h4KClgKioKClBvciBlamVtcGxvLCBjdWFuZG8gc2UgaGFjZSB1biBkaWFncmFtYSBkZSBjYWphIG8gYm94cGxvdCwgbm8gc2UgcmVwcmVzZW50YW4gbGFzIG9ic2VydmFjaW9uZXMgb3JpZ2luYWxlcywgc2lubyBxdWUgc2UgbXVlc3RyYW4gNSBlc3RhZMOtc3RpY29zIHJlc3VtZW4gZGUgbGEgZGlzdHJpYnVjacOzbiBkZSBsb3MgZGF0b3M7IGVzIGRlY2lyLCBzZSB1dGlsaXphIHVuYSB0cmFuc2Zvcm1hY2nDs24gZXN0YWTDrXN0aWNhLiBDdWFuZG8gdXPDoWJhbW9zIGBnZW9tX3Ntb3RoKClgIHRhbXBvY28gcmVwcmVzZW50w6FiYW1vcyBjb24gw6lsIGxvcyBkYXRvcyBvcmlnaW5hbGVzLCBzaW5vIHVuYSB0cmFuc2Zvcm1hY2nDs24gZXN0YWTDrXN0aWNhIGRlIGVzdG9zLiBDb25jcmV0YW1lbnRlIGxhIHRyYW5zZm9ybWFjacOzbiBlc3RhZMOtc3RpY2EgcXVlIHV0aWxpemEgYGdlb21fc21vdGgoKWAgZXMgZ2Vuw6lyaWNhbWVudGUgdW4gInNtb290aGVyIiwgY2FsY3VsYSBtZWRpYW50ZSB1bmEgcm9sbGluZy13aW5kb3dzIGxhIG1lZGlhIGRlIHksIGNvbmRpY2lvbmFkYSBhIHguICAKCkNhZGEgZnVuY2nDs24gYGdlb21feHgoKWAgcXVlIHV0aWxpY2Vtb3MsIGVuIHJlYWxpZGFkIG5lY2VzaXRhIGRlIHVuIHN0YXRfeHgoKSwgZW50b25jZXMgwr9wb3IgcXXDqSBudW5jYSBsbyBoZW1vcyB1c2Fkby9lc3BlY2lmaWNhZG8gbm9zb3Ryb3M/IENvbiBgZ2dwbG90MmAgbGEgcmF6w7NuIGVzIGNhc2kgc2llbXByZSBsYSBtaXNtYTogcG9ycXVlIHVuZGVyIHRoZSBob29kIGBnZ3Bsb3QyYCBoYWNlIG11Y2hhcyBjb3NhcyBwb3Igbm9zb3Ryb3MuIEVuIGNvbmNyZXRvLCBjYWRhIHZleiBxdWUgdXNhbW9zIHVuIGBnZW9tX3h4KClgIGVuIHJlYWxpZGFkIGBnZ3Bsb3QyYCBlc3TDoSBmaWphbmRvIHVuYSB0cmFuc2Zvcm1hY2nDs24gZXN0YWTDrXN0aWNhIHBvciBkZWZlY3RvIHBvciBub3NvdHJvcy4gR2dwbG90IGVzIHVuIHNpc3RlbWEgbXV5IGNvbXBsZXRvLCBwZXJvIGHDum4gYXPDrSwgdW5hIHZleiBsbyBlbnRpZW5kZXMsIGhhY2VyIGdyw6FmaWNvcyBjb24gZWwgZXMgcmVsYXRpdmFtZW50ZSBmw6FjaWwgeSByw6FwaWRvIHBvcnF1ZSBtdWNoYXMgZGUgc3VzIG9wY2lvbmVzIG9wY2lvbmVzIG5vIGhhY2UgZmFsdGEgZXNwZWNpZmljYXJsYXMgZGlyZWN0YW1lbnRlLgoKClBvciBlamVtcGxvLCDCv2N1YWwgZXMgbGEgdHJhbnNmb3JtYWNpw7NuIGVzdGFkw61zdGljYSBxdWUgc2UgdXNhIHBvciBkZWZlY3RvIGVuIGdlb21fcG9pbnQoKT8gTmluZ3VuYSwgYnVlbm8sIGVuIHJlYWxpZGFkIHVzYSAgYHN0YXQgPSAiaWRlbnRpdHkiYCwgcGVybyBjb21vIGxvIGVzcGVjaWZpY2EgZ2dwbG90MiBhdXRvbcOhdGljYW1lbnRlLCBub3NvdHJvcyBubyBub3MgZGFtb3MgY3VlbnRhLgoKQ3VhbmRvIGhhY8OtYW1vcyBlc3RlIGdyw6FmaWNvOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGlyaXMsIGFlcyhQZXRhbC5MZW5ndGgsIFNlcGFsLkxlbmd0aCkpICsgZ2VvbV9wb2ludCgpIApgYGAKCkVuIHJlYWxpZGFkIGVzdMOhYmFtb3MgaGFjaWVuZG8KCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoaXJpcywgYWVzKFBldGFsLkxlbmd0aCwgU2VwYWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KHN0YXQgPSAiaWRlbnRpdHkiKQpgYGAKCsK/UXXDqSB0cmFuc2Zvcm1hY2lvbmVzIGVzdGFkw61zdGljYXMgcG9kZW1vcyBoYWNlciBjdWFuZG8gdXNlbW9zIGBnZW9tX3BvaW50KClgPyBQb3IgZWplbXBsbywgYGdlb21fcG9pbnQoc3RhdCA9ICJ1bmlxdWUiKWAgc8OzbG8gcmVwcmVzZW50YXLDrWEgbGFzIG9ic2VydmFjaW9uZXMgw7puaWNhcyBvIE5PIHJlcGV0aWRhcy4gRW4gZXN0ZSBjYXNvIGNyZW8gcXVlIGlyaXMgbm8gdGllbmUgb2JzZXJ2YWNpb25lcyByZXBldGlkYXMsIGFzw60gc2kgZWplY3V0w6FpcyBsYSBpbnN0cnVjY2nDs24gZGUgYWJham8gc2Ugc2VndWlyw6FuIHZpc3VhbGl6YW5kbyBsb3MgMTUwIGxpcmlvcy4KCmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChpcmlzLCBhZXMoUGV0YWwuTGVuZ3RoLCBTZXBhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoc3RhdCA9ICJ1bmlxdWUiKSAjLSBkZWphcsOtYSBzb2xvIG9ic2VydmFjaW9uZXMgbm8gcmVwZXRpZGFzCmBgYAoKClBvZGVtb3MgY29uc3VsdGFyIGxhcyBvcGNpb25lcyBwb3IgZGVmZWN0byBjb21wbGV0YXMgZGUgYGdlb21fcG9pbnQoKWAgW2FxdcOtXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2VvbV9wb2ludC5odG1sKQoKCgpDYWRhIGBnZW9tX3h4KClgIHRpZW5lIHVuIGRlZmF1bHQgc3RhdGlzdGljLCBwZXJvIHBvZGVtb3MgY2FtYmlhcmxvIHkgZXNwZWNpZmljYXIgb3RyYSBzdGF0IHBhcmEgYWRhcHRhcmxvIGEgbnVlc3RyYXMgbmVjZXNpZGFkZXMuIFBvciBlamVtcGxvLCB0aGUgZGVmYXVsdCBzdGF0aXN0aWMgZm9yIGBnZW9tX2JhcigpYCBpcyBgc3RhdF9iaW4oKWAgcGVybyBwb2RlbW9zIHVzYXIgb3RyYXMgc3RhdF94eC4gT3RyYSB2ZXogcGFyZWNlIHVuIHRyYWJhbGVuZ3VhcywgcGVybyBjdWFuZG8gbG8gZW50aWVuZGVzIGVzIHJlbGF0aXZhbWVudGUgc2VuY2lsbG8uIAoKUG9yIGVqZW1wbG8sIGVuIG51ZXN0cm8gZ3LDoWZpY28gZGUgcHVudG9zLCBwb2RlbW9zIHVzYXIgb3RyYXMgdHJhbnNmb3JtYWNpb25lcyBlc3RhZMOtc3RpY2FzLCB1bmEgZGUgbGFzIHF1ZSBtw6FzIHNlbnRpZG8gdGllbmUgZXMgY2FsY3VsYXIgbWVkaWFzIG3Ds3ZpbGVzIGNvbiB1biBtw6l0b2RvIGRlIGFsaXNhZG8gKHNtb290aGVyKQoKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwIDwtIGdncGxvdChpcmlzLCBhZXMoUGV0YWwuTGVuZ3RoLCBTZXBhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykpIAoKcCArIGdlb21fcG9pbnQoc3RhdCA9ICJpZGVudGl0eSIpCgpwICsgZ2VvbV9wb2ludChzdGF0ID0gInNtb290aCIsIG1ldGhvZCA9ICJhdXRvIikKYGBgCgoKQXVucXVlIGVuIGVzdGUgY2FzbyBlcyBiYXN0YW50ZSBtw6FzIGbDoWNpbCBoYWNlcmxvIGNvbjoKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKQpwICsgZ2VvbV9wb2ludCgpICsgc3RhdF9zbW9vdGgoKQoKcCArIGdlb21fcG9pbnQoKSArIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsICAgc2UgPSBGQUxTRSwgIHNpemUgPSAxKQpwICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgICBzZSA9IEZBTFNFLCAgc2l6ZSA9IDEpCgpwICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sID0gIiNDNDIxMjYiLCAgc2UgPSBGQUxTRSwgIHNpemUgPSAxKQpgYGAKCjxicj4KCkFsZ3VuYXMgdHJhbnNmb3JtYWNpb25lcyBlc3RhZMOtc3RpY2FzIMO6dGlsZXMgeSBlbiBxdcOpIGdlb21zIGVzdMOhbiBkaXNwb25pYmxlczoKCiAgICAtIHN0YXRfYmluKCk6IGdlb21fYmFyKCksIGdlb21fZnJlcXBvbHkoKSwgZ2VvbV9oaXN0b2dyYW0oKSAgCiAgICAtIHN0YXRfYmluMmQoKTogZ2VvbV9iaW4yZCgpICAKICAgIC0gc3RhdF9iaW5kb3QoKTogZ2VvbV9kb3RwbG90KCkgIAogICAgLSBzdGF0X2JpbmhleCgpOiBnZW9tX2hleCgpICAgCiAgICAtIHN0YXRfYm94cGxvdCgpOiBnZW9tX2JveHBsb3QoKSAgICAKICAgIC0gc3RhdF9jb250b3VyKCk6IGdlb21fY29udG91cigpICAKICAgIC0gc3RhdF9xdWFudGlsZSgpOiBnZW9tX3F1YW50aWxlKCkgICAKICAgIC0gc3RhdF9zbW9vdGgoKTogZ2VvbV9zbW9vdGgoKSAgCiAgICAtIHN0YXRfc3VtKCk6IGdlb21fY291bnQoKSAgIAoKRXMgcmFybyBxdWUgdGVuZ2Ftb3MgcXVlIHVzYXIgZXN0YXMgZnVuY2lvbmVzIGBzdGF0c194eCgpYCBkaXJlY3RhbWVudGUsIHBlcm8gc2kgcXVpZXJlcyB2ZXIgcXVlIGhhY2VuIGV4YWN0YW1lbnRlIGNvbnZpZW5lIGNvbnN1bHRhciBsYSBkb2N1bWVudGFjacOzbiBwYXJhIHZlciBxdcOpIGhhY2UgeSBjw7NtbyBzZSBhcGxpY2EgZXhhY3RhbWVudGUgY2FkYSB0cmFuc2Zvcm1hY2nDs24gZXN0YWTDrXN0aWNhIGEgbG9zIGRhdG9zLgoKSGF5IG90cmFzIGZ1bmNpb25lcyBgc3RhdF94eCgpYCBxdWUgbm8gc2UgcHVlZGVuIHV0aWxpemFyIGNvbiBsYXMgZnVuY2lvbmVzIGBnZW9tX3h4KClgOgoKICAgIC0gc3RhdF9lY2RmKCk6IGNvbXB1dGUgYSBlbXBpcmljYWwgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gcGxvdC4gIAogICAgLSBzdGF0X2Z1bmN0aW9uKCk6IGNvbXB1dGUgeSB2YWx1ZXMgZnJvbSBhIGZ1bmN0aW9uIG9mIHggdmFsdWVzLiAgCiAgICAtIHN0YXRfc3VtbWFyeSgpOiBzdW1tYXJpc2UgeSB2YWx1ZXMgYXQgZGlzdGluY3QgeCB2YWx1ZXMuICAKICAgIC0gc3RhdF9zdW1tYXJ5MmQoKSwgc3RhdF9zdW1tYXJ5X2hleCgpOiBzdW1tYXJpc2UgYmlubmVkIHZhbHVlcy4gIAogICAgLSBzdGF0X3FxKCk6IHBlcmZvcm0gY2FsY3VsYXRpb25zIGZvciBhIHF1YW50aWxlLXF1YW50aWxlIHBsb3QuICAKICAgIC0gc3RhdF9zcG9rZSgpOiBjb252ZXJ0IGFuZ2xlIGFuZCByYWRpdXMgdG8gcG9zaXRpb24uICAKICAgIC0gc3RhdF91bmlxdWUoKTogcmVtb3ZlIGR1cGxpY2F0ZWQgcm93cy4gIAoKPGJyPgoKIyMjIyBBbGd1bm9zIGVqZW1wbG9zCgpWZWFtb3MgYWxndW5vcyBlamVtcGxvcyDDunRpbGVzOgoKMS4gRW4gdW4gZGlhZ3JhbWEgZGUgY2FqYSBtb3N0cmFyIGxhIG1lZGlhOgogClF1ZXJlbW9zIHF1ZSBsYSBtZWRpYSBzZSByZXByZXNlbnRlIGNvbW8gdW4gcHVudG8sIGVudG9uY2VzIHVzYW1vcyBgZ2VvbV9wb2ludCgpYCBwZXJvIG5vIHF1ZXJlbW9zIHJlcHJlc2VudGFyIGxvcyB2YWxvcmVzIG9yaWdpbmFsZXMsIHNpbm8gbGEgbWVkaWEsIGFzw60gcXVlIGRlbnRybyBkZSBnZW9tIHVzYW1vcyBsYSBvcGNpw7NuIGBzdGF0ID0gc3VtbWFyeWAKIApgYGB7cn0KZ2dwbG90KGlyaXMsIGFlcyhTcGVjaWVzLCBTZXBhbC5MZW5ndGgpKSArIAogIGdlb21fYm94cGxvdCgpICsgCiAgZ2VvbV9wb2ludChzdGF0ID0gInN1bW1hcnkiLCBmdW4ueSA9ICJtZWFuIiwgY29sb3VyID0gInJlZCIsIHNpemUgPSA0KQpgYGAKClNlIGNvbnNlZ3VpcsOtYSBsb3MgbWlzbW8gdXNhbmRvIGRpcmVjdGFtZW50ZSBsYSAgZnVuY2nDs24gYHN0YXRfc3VtbWFyeSgpYCBjb24gbGEgb3BjacOzbiBnZW9tID0gInBvaW50Ii4gU8OtLCBlbiBgZ2dwbG90MmAgbGFzIGNvc2FzIHNlIHB1ZWRlbiBoYWNlciBkZSB2YXJpYXMgbWFuZXJhcyEhCgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGlyaXMsIGFlcyhTcGVjaWVzLCBTZXBhbC5MZW5ndGgpKSArIAogIGdlb21fYm94cGxvdCgpICsgCiAgc3RhdF9zdW1tYXJ5KGdlb20gPSAicG9pbnQiLCBmdW4ueSA9ICJtZWFuIiwgY29sb3VyID0gInJlZCIsIHNpemUgPSA0KQpgYGAKCjIuICoqT3RybyBlamVtcGxvOioqIGVsIGRlZmF1bHQgc3RhdCBkZSBnZW9tX2hpc3RvZ3JhbSBlcyBzdGF0ID0gImJpbiIsIG1vc3RyYW5kb25vcyBlbCBuw7ptZXJvIGRlIG9ic2VydmFjaW9uZXMgZW4gY2FkYSBiaW4uIFNpIHF1ZXJlbW9zIHF1ZSBub3MgbXVlc3RyZSBmcmVjdWVuY2lhcyByZWxhdMOtdmFzIGFsIGdydXBvIG8gYmluIG3DoXMgbnVtZXJvc286IAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgpKSArIGdlb21faGlzdG9ncmFtKCkgCgpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCkpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSBzdGF0KGNvdW50IC8gbWF4KGNvdW50KSkpKQpgYGAKCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICI5MCUiLCAgZmlnLmFzcCA9IDUvOX0KcDEgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgpKSArIGdlb21faGlzdG9ncmFtKCkgCnAyIDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoKSkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IHN0YXQoY291bnQgLyBtYXgoY291bnQpKSkpCnAxICsgcDIgICsgcGxvdF9sYXlvdXQobmNvbCA9IDIpCmBgYAoKCgo8YnI+CgoqKkJvbnVzOioqIENvbiBgc3RhdF9mdW5jdGlvbigpYCBwb2RlbW9zIGRpYnVqYXIgY3VydmFzIGRlIGRlbnNpZGFkOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZGYgPC0gdGliYmxlKHggPSBjKC0yMCwgMjApKQogIApnZ3Bsb3QoZGYsIGFlcyh4ID0geCkpICsKc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QobWVhbiA9IDAsIHNkID0gNSksIGNvbG9yID0gImJsYWNrIikgKwpzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBhcmdzID0gbGlzdChtZWFuID0gMCwgc2QgPSAxKSwgY29sb3IgPSAicmVkIikgKwpzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBhcmdzID0gbGlzdChtZWFuID0gMCwgc2QgPSAzKSwgY29sb3IgPSAiYmx1ZSIpCmBgYAoKCjxicj4KCiMjIyAzLjggUG9zaXRpb24gYWRqdXN0bWVudHMKCgo+IEFsbCBsYXllcnMgaGF2ZSBhIHBvc2l0aW9uIGFkanVzdG1lbnQgdGhhdCByZXNvbHZlcyBvdmVybGFwcGluZyBnZW9tcy4gT3ZlcnJpZGUgdGhlIGRlZmF1bHQgYnkgdXNpbmcgdGhlIHBvc2l0aW9uIGFyZ3VtZW50IHRvIHRoZSBnZW9tXyBvciBzdGF0XyBmdW5jdGlvbi4KCgpMb3MgYWp1c3RlcyBkZSBwb3NpY2nDs24gYWZlY3RhbiBhIGxhIHBvc2ljacOzbiBkZSBsb3MgZWxlbWVudG9zIGRlIHVuYSBjYXBhLiBMb3MgZ3LDoWZpY29zIGVuIGxvcyBxdWUgbcOhcyBzZSB1dGlsaXphbiBsb3MgYWp1c3RlcyBkZSBwb3NpY2nDs24gc29uIGxvcyBncsOhZmljb3MgZGUgYmFycmFzLiBTdSBwb3NpY2nDs24gcG9yIGRlZmVjdG8gZXMgcG9zaXRpb24gPSAic3RhY2siLiBTZSBwdWVkZW4gY2FtYmlhciBjb24gZWwgYXJndW1lbnRvIGdlb21fYmFyKHBvc2l0aW9uID0gInh4eHgiKWAgLCBhdW5xdWUgc2kgdXNhcyBsYXMgZnVuY2lvbmVzIGBwb3NpdGlvbl94eCgpYCB0aWVuZXMgbcOhcyBmbGV4aWJpbGlkYWQ6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoaXJpcyAsIGFlcyhTcGVjaWVzKSkgKyBnZW9tX2JhcigpCmdncGxvdChtdGNhcnMsIGFlcyhjeWwpKSArICBnZW9tX2JhcigpCmBgYAoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICI5MCUiLCAgZmlnLmFzcCA9IDUvOX0KcDEgPC0gZ2dwbG90KGlyaXMgLCBhZXMoU3BlY2llcykpICsgZ2VvbV9iYXIoKSAgIy0gdGVtYSBwb3IgZGVmZWN0bwpwMiA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoY3lsKSkgKyAgZ2VvbV9iYXIoKQoKcDEgKyBwMiAgKyBwbG90X2xheW91dChuY29sID0gMikKYGBgCgpQYXJhIHBvZGVyIHZpc3VhbGl6YXIgZ3LDoWZpY29zIGRlIGJhcnJhcyBjb24gMiB2YXJpYWJsZXMsdGVuZW1vcyBxdWUgdXNhciBvdHJvIGRhdGFzZXQ6IGBtdGNhcnNgCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QobXRjYXJzLCBhZXMoZmFjdG9yKGN5bCksIGZpbGwgPSBmYWN0b3IodnMpKSkgKyBnZW9tX2JhcigpICMtIHBvcwpnZ3Bsb3QobXRjYXJzLCBhZXMoZmFjdG9yKGN5bCksIGZpbGwgPSBmYWN0b3IodnMpKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikKZ2dwbG90KG10Y2FycywgYWVzKGZhY3RvcihjeWwpLCBmaWxsID0gZmFjdG9yKHZzKSkpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKQpnZ3Bsb3QobXRjYXJzLCBhZXMoZmFjdG9yKGN5bCksIGZpbGwgPSBmYWN0b3IodnMpKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlMihwcmVzZXJ2ZSA9ICJzaW5nbGUiKSkKYGBgCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjEwMCUiLCAgZmlnLmFzcCA9IDUvOX0KcDEgPC0gZ2dwbG90KG10Y2FycywgYWVzKGZhY3RvcihjeWwpLCBmaWxsID0gZmFjdG9yKHZzKSkpICsgZ2VvbV9iYXIoKSAjLSBwb3MKcDIgPC0gZ2dwbG90KG10Y2FycywgYWVzKGZhY3RvcihjeWwpLCBmaWxsID0gZmFjdG9yKHZzKSkpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpCnAzIDwtIGdncGxvdChtdGNhcnMsIGFlcyhmYWN0b3IoY3lsKSwgZmlsbCA9IGZhY3Rvcih2cykpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIikKcDQgPC0gZ2dwbG90KG10Y2FycywgYWVzKGZhY3RvcihjeWwpLCBmaWxsID0gZmFjdG9yKHZzKSkpICsgZ2VvbV9iYXIocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZShwcmVzZXJ2ZSA9ICJzaW5nbGUiKSkKCnAxICsgcDIgKyBwMyArIHA0ICsgcGxvdF9sYXlvdXQobmNvbCA9IDIpCmBgYAoKPGJyPgoKUG9yIGVqZW1wbG8sIHBvZGVtb3MgbW9kaWZpY2FyIGxhIHBvc2ljacOzbiBwb3IgZGVmZWN0byBkZSBudWVzdHJvIGdyw6FmaWNvIGRlIHB1bnRvcyBjb24gZWwgaXJpcyBkYXRhc2V0IHVzYW5kbyBkb3MgZW5mb3F1ZXM6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3BvaW50KHBvc2l0aW9uID0gImppdHRlciIsIGNvbG9yID0gInBpbmsiKSAKZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9qaXR0ZXIoIGNvbG9yID0gInBpbmsiKSAKYGBgCgoKCmBgYHtyLCBlY2hvID0gRkFMU0V9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKSArIGdlb21fcG9pbnQocG9zaXRpb24gPSAiaml0dGVyIiwgY29sb3IgPSAicGluayIpIApgYGAKCmBnZW9tX2ppdHRlcigpYCwgbyBhbHRlcm5hdGl2YW1lbnRlIGBnZW9tX3BvaW50KHBvc2l0aW9uID0gImppdHRlciIpYCBjYW1iaWEgbGEgcG9zaWNpw7NuIG9yaWdpbmFsIGRlIGxvcyBkYXRvcyBhw7FhZGllbmRvIHVuIHBvY28gZGUgcnVpZG8sIGhhY2llbmRvIHF1ZSBzZSBkZXNwbGFjZW4gdW4gcG9jby4gRXN0YSB0w6ljbmljYSBzZSB1c2EgbXVjaG9zIGN1YW5kbyBoYXkgbXVjaG9zIGRhdG9zIHNpbWlsYXJlcyAob3ZlcnBsb3R0aW5nKS4KCgo8YnI+CgojIyMgMy45IENvb3JkZW5hZGFzCgo+IFRoZSBjb29yZGluYXRlIHN5c3RlbSBkZXRlcm1pbmVzIGhvdyB0aGUgeCBhbmQgeSBhZXN0aGV0aWNzIGNvbWJpbmUgdG8gcG9zaXRpb24gZWxlbWVudHMgaW4gdGhlIHBsb3QuIFRoZSBkZWZhdWx0IGNvb3JkaW5hdGUgc3lzdGVtIGlzIENhcnRlc2lhbiAoY29vcmRfY2FydGVzaWFuKCkpLCB3aGljaCBjYW4gYmUgdHdlYWtlZCB3aXRoIGNvb3JkX21hcCgpLCBjb29yZF9maXhlZCgpLCBjb29yZF9mbGlwKCksIGFuZCBjb29yZF90cmFucygpLCBvciBjb21wbGV0ZWx5IHJlcGxhY2VkIHdpdGggY29vcmRfcG9sYXIoKQoKClBvciBlamVtcGxvOiBgY29vcmRfZml4ZWQoKWAuIEVuIG51ZXN0cm8gZ3LDoWZpY28sIHRhbnRvIGxhIGxvbmdpdHVkIGRlbCBww6l0YWxvIGNvbW8gZGVsIHPDqXBhbG8gc2UgbWlkZW4gZW4gbGFzIG1pc21hcyB1bmlkYWRlcywgYXPDrSBxdWUgc3UgcmF0aW8gaW1wbMOtY2l0byBlcyAxIGEgMS4gQ2FtYmllbW9zIGVsIHJhdGlvIGRlIGxhcyBjb29yZGVuYWRhcyBjb24gYGNvb3JkX2ZpeGVkKClgCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwIDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKSAKCnAgKyBjb29yZF9maXhlZChyYXRpbyA9IDEvMykKcCArIGNvb3JkX2ZpeGVkKHJhdGlvID0gMy8xKQpgYGAKCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjEwMCUiLCAgZmlnLmFzcCA9IDUvOX0KcDEgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9wb2ludCgpIApwMiA8LSBwMSArIGNvb3JkX2ZpeGVkKHJhdGlvID0gMS8zKQpwMyA8LSBwMSArIGNvb3JkX2ZpeGVkKHJhdGlvID0gMy8xKQoKcDIgKyBwMyArIHBsb3RfbGF5b3V0KG5yb3cgPSAxKQojaHR0cHM6Ly93d3cuc3RhdHdvcnguY29tL2RlL2Jsb2cvY29vcmRpbmF0ZS1zeXN0ZW1zLWluLWdncGxvdDItZWFzaWx5LW92ZXJsb29rZWQtYW5kLXJhdGhlci11bmRlcnJhdGVkLwpgYGAKCgoKCjxicj4KCgojIyA0LiBDb21iaW5hbmRvIGdyw6FmaWNvcwoKCkxhIHTDqWNuaWNhIGRlbCBmYWNldGluZyBlcyB1bmEgZmFudMOhc3RpY2EgaGVycmFtaWVudGEgcGFyYSBkaXZpZGlyIHVuIGdyw6FmaWNvIGVuIHZhcmlvcywgZW4gcHJpbmNpcGlvIGVuIGZ1bmNpw7NuIGRlIHVuYSB2YXJpYWJsZSBjYXRlZ8OzcmljYSwgc2kgcXVlcmVtb3MgdXNhciB1bmEgdmFyaWFibGUgY29udGludWEgcHJpbWVybyBoYWJyw61hIHF1ZSBkaXNjcmV0aXphcmxhLiAqKlBFUk8qKiwgYSB2ZWNlcyBsbyBxdWUgaW50ZXJlc2EgZXMgY3JlYXIgdW5hIGZpZ3VyYSBjb21wdWVzdGEgZGUgdmFyaW9zIGdyw6FmaWNvcyBkaWZlcmVudGVzLiBFc3RvIG5vIGVzIHVuIGVsZW1lbnRvIG3DoXMgZGUgZ2dwbG90MiBlcyB1bmEgb3BlcmFjacOzbiBxdWUgaGFjZW1vcyBzb2JyZSB1biBncnVwbyBkZSB2YXJpb3MgZ3LDoWZpY29zLgoKClBvZGVtb3MgaGFjZXJsbyBjb24gdmFyaW9zIHBhcXVldGVzOgoKLSBjb24gZWwgcGFxdWV0ZSBgZ3JpZEV4dHJhYCB5IHN1IGZ1bmNpw7NuIGBncmlkLmFycmFuZ2UoKWAKCmBgYHtyLCBldmFsID0gVFJVRSwgb3V0LndpZHRoID0gIjgwJSJ9CmxpYnJhcnkoZ3JpZEV4dHJhKQpwMSA8LSBnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBjb2xvciA9IFNwZWNpZXMpKSArIGdlb21fcG9pbnQoKQpwMiA8LSBnZ3Bsb3QoaXJpcykrIGFlcyhTcGVjaWVzLCBTZXBhbC5MZW5ndGgpICsgZ2VvbV9ib3hwbG90KCkKCmdyaWQuYXJyYW5nZShwMSwgcDIsIG5jb2wgPSAyLCB3aWR0aHMgPSBjKDYuNSwgMy41KSkKYGBgCgpBZGVtw6FzIGRlIGxvcyBhcmd1bWVudG9zIG5jb2wsIG5yb3cgeSB3aWR0aHMsIGNvbiAgYGdyaWRFeHRyYWAgc2UgcHVlZGVuIGhhY2VyIGNvbXBvc2ljaW9uZXMgW23DoXMgY29tcGxlamFzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ3JpZEV4dHJhL3ZpZ25ldHRlcy9hcnJhbmdlR3JvYi5odG1sKQoKCi0gY29uIGVsIHBhcXVldGUgW2BwYXRjaHdvcmtgXShodHRwczovL2dpdGh1Yi5jb20vdGhvbWFzcDg1L3BhdGNod29yaykgCgoKCmBgYHtyLCBldmFsID0gVFJVRSwgb3V0LndpZHRoID0gIjgwJSJ9CmxpYnJhcnkocGF0Y2h3b3JrKQpwMSArIHAyICsgcGxvdF9sYXlvdXQobmNvbCA9IDIpCmBgYAoKLSBjb24gZWwgcGFxdWV0ZSBbYGNvd3Bsb3RgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvY293cGxvdC92aWduZXR0ZXMvaW50cm9kdWN0aW9uLmh0bWwpLiBFc3RlIHBhcXVldGUgdGFtYmnDqW4gc2UgcHVlZGUgdXRpbGl6YXIgcGFyYSBoYWNlciBhbm90YWNpb25lcyBvIHBhcmEgaW5jb3Jwb3JhciBpbcOhZ2VuZXMgYSBudWVzdHJvcyBncsOhZmljb3MKCgpgYGB7cn0KbGlicmFyeShjb3dwbG90KQpnZ2RyYXcocDEpICsgZHJhd19pbWFnZShoZXJlOjpoZXJlKCIuL2ltYWdlbmVzL0NhcHR1cmEuSlBHIiksIAogICAgICAgICAgICAgICB4ID0gMSwgeSA9IDEsIGhqdXN0ID0gMSwgdmp1c3QgPSAxLCB3aWR0aCA9IDAuMzMsIGhlaWdodCA9IDAuNDIpCmBgYAoKCjxicj4KCgojIyA1LiBFeHBvcnRhbmRvIGdyw6FmaWNvcwoKTGEgbWF5b3LDrWEgZGUgbGFzIHZlY2VzLCBjdWFuZG8gY3JlYW1vcyB1biBncsOhZmljbyBsbyB2ZW1vcyBpbm1lZGlhdGFtZW50ZSwgcGVybyB0YW1iacOpbiBwb2RlbW9zIGFzaWduYXJsZSB1biBub21icmUgeSBtYW5pcHVsYXJsbyBtw6FzIGFkZWxhbnRlLiAKCmBgYHtyLCBldmFsID0gRkFMU0V9CnAgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9wb2ludCgpCnAgKyBnZW9tX2xpbmUoKQpgYGAKCgpVbmEgdmV6IHF1ZSB0aWVuZXMgZWwgZ3LDoWZpY28gZ3VhcmRhZG8gY29tbyB1biBvYmpldG8gZW4gZWwgUiBlbnZpcm9ubWVudCBwdWVkZXMgaGFjZXIgdmFyaWFzIGNvc2FzIGNvbiDDqWw6CgoxLiB2ZXJsbyBlbiBsYSBwYW50YWxsYSBjb24gYHByaW50KClgIG8gbGxhbW5hZG9sbyBjb24gInN1IG5vbWJyZSIuCgoyLiBndWFyZGFybG8gZW4gdW4gZmljaGVybyBlbiBkaWZlcmVudGVzIGZvcm1hdG9zLiBQaWVuc2EgcXVlIGVzdGFtb3MgZ3VhcmRhbmRvIGVsIGdyw6FmaWNvLCBsYSBpbWFnZW4sIGxhIHJlcHJlc2VudGFjacOzbiBkZWwgZ3LDoWZpY28sIG5vIGVsIG9iamV0byBSLgoKICAgIC0gUG9kZW1vcyB1c2FyIGxhICJFeHBvcnQgVGFiIiBlbiBlbCBQbG90IFBhbmUgZGUgUnN0dWRpby4gTG8gZ3JhYmFyw6EgZW4gYmFqYSByZXNvbHVjacOzbi4gIFRhbWJpw6luIHBvZGVtb3MgY2FtYmlhciBsYXMgZGltZW5zaW9uZXMgKGFuY2h1cmEgeSBhbHR1cmEpIGRlbCBncsOhZmljby4gU2kgdmFtb3MgYSB1dGlsaXphciBlbCBncsOhZmljbyBlbiBXb3JkIGVzIGNvbnZlbmllbnRlIGd1YXJkYXIgZWwgZ3LDoWZpY28gY29tbyBNZXRhZmlsZS4KICAgIAogICAgLSBDb21vIGFsdGVybmF0aXZhIHBvZGVtb3MgdXNhciBsYSBmdW5jacOzbiBgZ2dzYXZlKClgIHF1ZSwgYWRlbWFzLCBub3MgcGVybWl0ZSBjYW1iaWFyIGVsIHRhbWHDsW8geSBsYSByZXNvbHVjacOzbiBkZWwgZ3LDoWZpY28gY29uIGxvcyBhcmd1bWVudG9zIHdpZHRoLCBoZWlnaHQgeSBkcGkuIE5vdGE6IExvcyBwYXLDoW1ldHJvcyB3aWR0aCBhbmQgaGVpZ2h0IHRhbWJpw6luIGRldGVybWluYW4gZWwgdGFtYcOxbyBkZSBsYSBmdWVudGUgZGVsIGdyw6FmaWNvIGd1YXJkYWRvLgoKICAgIAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dzYXZlKCIuL2dyYWZfb3V0L215X2dyYWZpY29fY2h1bG8ucG5nIiwgcCwgd2lkdGggPSAxNSwgaGVpZ2h0ID0gMTApCmdnc2F2ZSgiZmlsZW5hbWUucG5nIiwgcGxvdCA9IG15X3Bsb3QsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iLCBkcGkgPSAicmV0aW5hIikKCiMgVGIgZnVuY2lvbmEgcGFyYSBmaWd1cmFzIGNvbXB1ZXN0YXMgZGUgdmFyaW9zIGdyw6FmaWNvcwpncmFmaWNvX2NvbWJpbmFkbyA8LSBncmlkLmFycmFuZ2UocDEsIHAyLCBuY29sID0gMiwgd2lkdGhzID0gYyg2LCA0KSkKZ2dzYXZlKCJmaWdfb3V0cHV0L215X2NvbWJvX3Bsb3QucG5nIiwgZ3JhZmljb19jb21iaW5hZG8sIHdpZHRoID0gMTAsIGRwaSA9IDMwMCkKYGBgCgoKLSBGaW5hbG1lbnRlLCBwb2RlbW9zIGd1YXJkYXIgdW5hIGNvcGlhIGNvbXBsZXRhIGRlbCBncsOhZmljbywgbm8gZGUgc3UgcmVwcmVzZW50YWNpw7NuIHZpc3VhbCwgc2lubyBkZWwgb2JqZXRvIFIgY29uIGBzYXZlUkRTKClgLCBwYXJhIGx1ZWdvIGxlZXJsbyBjb24gYHJlYWRSRFMoKWAuIEF1bnF1ZSwgeWEgcXVlIG5vcyBwb25lbW9zLCBlcyBtZWpvciBndWFyZGFyIGVsIHNjcmlwdCBxdWUgZ2VuZXJhIGVsIG9iamV0by4gU8OzbG8gdGVuZHLDrWEgc2VudGlkbyBzaSAgZnVlc2UgbXV5IGNvc3Rvc28sIHBvciBlamVtcGxvIGVuIHTDqXJtaW5vcyBkZSB0aWVtcG8sIHJlcHJvZHVjaXIgZWwgZ3LDoWZpY28uCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpzYXZlUkRTKHAsICJwbG90LnJkcyIpCm15X3ZhbGlvc29fZ3JhZmljbyA8LSByZWFkUkRTKCJwbG90LnJkcyIpCmBgYAoKCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyA2LiBUaXBvcyBkZSBncsOhZmljb3MKCkVuIGVzdMOhIHNlY2Npw7NuIHByZXNlbnRhcmVtb3MgYWxndW5vcyBlamVtcGxvcyBkZSBhbGd1bm9zIGRlIGxvcyBncsOhZmljb3MgbcOhcyB1dGlsaXphZG9zIGVuIGVsIGFuw6FsaXNpcyBkZSBkYXRvcy4gUHVlZGVzIHZlciBsaXN0YWRvcyBtw6FzIGNvbXBsZXRvcyBlbjoKCiAgLSBbUi1ncmFwaCBnYWxsZXJ5XShodHRwczovL3d3dy5yLWdyYXBoLWdhbGxlcnkuY29tLyk6IEltcHJlc2lvbmFudGUhIQoKCiAgLSBbci1zdGF0aXN0aWNzLmNvXShodHRwOi8vci1zdGF0aXN0aWNzLmNvL1RvcDUwLUdncGxvdDItVmlzdWFsaXphdGlvbnMtTWFzdGVyTGlzdC1SLUNvZGUuaHRtbCk6IG3DoXMgYsOhc2ljbyBwZXJvIG11eSBkaWTDoWN0aWNvLgoKICAKICAtIFtFeHRlbnNpb25lcyBnZ3Bsb3QyXShodHRwOi8vd3d3LmdncGxvdDItZXh0cy5vcmcvZ2FsbGVyeS8pOiBJbXByZXNpb25hbnRlISEKCiAgLSBbUi1ncmFwaCBnYWxsZXJ5IC0gZ2dwbG90MiBwYWNrYWdlXShodHRwOi8vci1ncmFwaC1nYWxsZXJ5LmNvbS9nZ3Bsb3QyLXBhY2thZ2UuaHRtbCk6IG90cmEgdmV6LCBpbXByZXNpb25hbnRlISEKCgo8YnI+CgojIyMgNi4xIEhpc3RvZ3JhbWFzCgpTZSB1dGlsaXphbiBwYXJhIG1vc3RyYXIgbGEgKipkaXN0cmlidWNpw7NuIGRlIFVOQSB2YXJpYWJsZSBjb250aW51YSoqLCBwb3IgZWplbXBsbyBsYSBsb25naXR1ZCBkZWwgc8OpcGFsbyAoU2VwYWwuTGVuZ3RoKS4KClBhcmEgaGFjZXIgaGlzdG9ncmFtYXMgZW4gZ2dwbG90MiBzZSB1dGlsaXphIGBnZW9tX2hpc3RvZ3JhbWAgeSwgYSB2ZWNlcywgYGdlb21fZnJlcXBvbHkoKWAuIExvcyBkb3MgZ2VvbXMgdHJhYmFqYW4gZGUgbGEgbWlzbWEgbWFuZXJhLCBkaXZpZGVuIGxhIHZhcmlhYmxlIHggZW4gaW50ZXJ2YWxvcyB5IGN1ZW50YW4gbGFzIG9ic2VydmFjaW9uZXMgZW4gY2FkYSBpbnRlcnZhbG8sIHBhcmEgbW9zdHJhcmxhcyBlbiBlbCBlamUgWS4gTGEgZGlmZXJlbmNpYSBlbnRyZSBlbGxvcyBlcyBxdWUgYGdlb21faGlzdG9ncmFtYCB1dGlsaXphIGJhcnJhcyBwYXJhIG1vc3RyYXIgZWwgbsO6bWVybyBkZSBvYnNlcnZhY2lvbmVzIG8gZnJlY3VlbmNpYSBhYnNvbHV0YSwgbWllbnRyYXMgcXVlICBgZ2VvbV9mcmVxcG9seSgpYCB1c2EgbGluZWFzLiAKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCkpICsgZ2VvbV9oaXN0b2dyYW0oKSAKYGBgCgpDb21vIHZlcywgbG8gcXVlIGhhY2UgdW4gaGlzdG9ncmFtYSBlcyBkaXZpZGlyIGVsIGVqZSBYIGVuIGludGVydmFsb3MgbyAiYmlucyIgeSBtb3N0cmFyIGVuIGVsIGVqZSBZIGVsIG7Dum1lcm8gZGUgb2JzZXJ2YWNpb25lcyBkZSB4IHF1ZSBjYWVuIGVuIGNhZGEgImJpbiIuIEFkZW3DoXMsIGdncGxvdDIgbm9zIGF2aXNhIGRlIHF1ZSBwb3IgZGVmZWN0byBzZSBkaXZpZGUgZWwgZWplIFggZW4gMzAgaW50ZXJ2YWxvcywgcGVybyBxdWUgcG9kZW1vcyBjYW1iaWFybG8gY29uIGBiaW5zID0gbXlfbl9kZV9iaW5zYC4gCgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcCA8LSBnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCkpCnAgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNDApICsgeGxhYigiNDAgaW50ZXJ2YWxvcyIpCnAgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNCkgKyB4bGFiKCJTw7NsbyA0IGludGVydmFsb3MiKQpgYGAKCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiOTUlIn0KcCA8LSBnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCkpCnAxIDwtIHAgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNDApICsgeGxhYigiNDAgaW50ZXJ2YWxvcyIpCnAyIDwtIHAgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNCkgKyB4bGFiKCJTw7NsbyA0IGludGVydmFsb3MiKQpwMSArIHAyICsgcGxvdF9sYXlvdXQobmNvbCA9IDIpCmBgYAoKCk90cmEgb3BjacOzbiBpbnRlcmVzYW50ZSBlcyBlbGVnaXIgbGEgYW5jaHVyYSBkZWwgaW50ZXJ2YWxvIGNvbiBsYSBvcGNpw7NuIGBiaW53aWR0aGAuCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwIDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoKSkKcCArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4xKSArIHhsYWIoIkhlIGVsZWdpZG8gbGEgYW5jaHVyYSA9IDAuMSIpCnAgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEuMSkgKyB4bGFiKCJFc3RhIHZleiBsYSBhbmNodXJhID0gMS4xIikKYGBgCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjk1JSJ9CnAgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgpKQpwMSA8LSBwICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjEpICsgeGxhYigiSGUgZWxlZ2lkbyBsYSBhbmNodXJhID0gMC4xIikKcDIgPC0gcCArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMS4xKSArIHhsYWIoIkVzdGEgdmV6IGxhIGFuY2h1cmEgPSAxLjEiKQpwMSArIHAyICsgcGxvdF9sYXlvdXQobmNvbCA9IDIpCmBgYAoKClNpIGVuIGVsIGVqZSBZIHF1ZXJlbW9zIGZyZWN1ZW5jaWFzIHJlbGF0aXZhcyBvIHBvcmNlbnRhamVzIGVuIGx1Z2FyIGRlIGNvdW50cywgcG9kZW1vcyBoYWNlcmxvIGNvbjoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSBzdGF0KGNvdW50KSAvIHN1bShjb3VudCkpLCBiaW5zID0gMTApICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KQpgYGAKCgpTZSBwdWVkZSBtZWpvcmFyIGJhc3RhbnRlIGxhIGFwYXJpZW5jaWEgZGVsIGhpc3RvZ3JhbWEganVnYW5kbyBjb24gbG9zIGNvbG9yZXMgeSBvcGNpb25lczoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCkpICsgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAidG9tYXRvIikKYGBgCgoKClNpIGFzb2NpYW1vcy9tYXBlYW1vcyBsYSB2YXJpYWJsZSBTcGVjaWVzIGEgYWxndW5hIGVzdMOpdGljYSwgcG9yIGVqZW1wbG8gYSBsYSBlc3TDqXRpY2EgImZpbGwiIG8gcmVsbGVubyBkZSBsYXMgYmFycmFzIGNvbiBgYWVzKGZpbGwgPSBTcGVjaWVzKWAsIHNlIHZpc3VhbGl6YXLDoSBxdcOpIHBhcnRlIGRlIGNhZGEgaW50ZXJ2YWxvIHBlcnRlbmVjZSBhIGNhZGEgY2xhc2UgZGUgbGlyaW8uCgoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCkpICsgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwLCBhZXMoZmlsbCA9IFNwZWNpZXMpLCBjb2xvciA9ICJibGFjayIpCmBgYAoKCkV2aWRlbnRlbWVudGUsIHRhbWJpw6luIHBvZGVtb3MgdmlzdWFsaXphciBjb21vIGNhbWJpYSBsYSBkaXN0cmlidWNpw7NuIGRlIGxhIGFuY2h1cmEgZGVsIHPDqXBhbG8gZW50cmUgbG9zIDMgdGlwb3MgZGUgbGlyaW9zIHNpIGhhY2Vtb3MgdW4gc21hbGwgbXVsdGlwbGU6CgoKYGBge3J9CnAgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIGZpbGwgPSBTcGVjaWVzKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAsIGNvbG9yID0gImJsYWNrIikKcCArIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoU3BlY2llcykpCmBgYAoKUGFyYSBtZWpvcmFyIGxhIHZpc3VhbGl6YWNpw7NuIHBvZGVtb3MgcG9uZXIgZW4gZWwgZm9uZG8gZWwgaGlzdG9ncmFtYSBwYXJhIHRvZG9zIGxvcyBkYXRvcy4gTG8gYXByZW5kw60gW2FxdcOtXShodHRwczovL2Ryc2ltb25qLnN2YnRsZS5jb20vcGxvdHRpbmctYmFja2dyb3VuZC1kYXRhLWZvci1ncm91cHMtd2l0aC1nZ3Bsb3QyKSwgYXVucXVlIGFob3JhIG1pc21vIGhheSB1biBwYXF1ZXRlIHBhcmEgaW1wbGVtZW50YXIgZXN0YSB0w6ljbmljYSwgZXMgZWwgcGFxdWV0ZSBbYGdnaGlnaGxpZ2h0YF0oaHR0cHM6Ly95dXRhbm5paGlsYXRpb24uZ2l0aHViLmlvL2dnaGlnaGxpZ2h0L2luZGV4Lmh0bWwpLgoKCmBgYHtyfQppcmlzX2JhY2tncm91bmcgPC0gaXJpcyAlPiUgc2VsZWN0KC1TcGVjaWVzKQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTZXBhbC5MZW5ndGgpKSArCiAgZ2VvbV9oaXN0b2dyYW0oZGF0YSA9IGlyaXNfYmFja2dyb3VuZywgZmlsbCA9ICJncmV5IiwgYmlucyA9IDE1KSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKGZpbGwgPSBTcGVjaWVzKSwgYmlucyA9IDE1KSArCiAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhTcGVjaWVzKSkKYGBgCgo8YnI+CgojIyMjIGdlb21fZGVuc2l0eSgpCgpVbmEgYWx0ZXJuYXRpdmEgYSBsb3MgaGlzdG9ncmFtYXMgc29uIGxvcyBncsOhZmljb3MgZGUgZGVuc2lkYWQgY29uIGBnZW9tX2RlbnNpdHkoKWAuIFBlcm8sIHNlZ8O6biBIYWRsZXk6IAoKPnRoZXkgYXJlIGhhcmRlciB0byBpbnRlcnByZXQgc2luY2UgdGhlIHVuZGVybHlpbmcgY29tcHV0YXRpb25zIGFyZSBtb3JlIGNvbXBsZXguIFRoZXkgYWxzbyBtYWtlIGFzc3VtcHRpb25zIHRoYXQgYXJlIG5vdCB0cnVlIGZvciBhbGwgZGF0YSwgbmFtZWx5IHRoYXQgdGhlIHVuZGVybHlpbmcgZGlzdHJpYnV0aW9uIGlzIGNvbnRpbnVvdXMsIHVuYm91bmRlZCwgYW5kIHNtb290aC4KCkNvbW8gZGljZSBIYWRsZXksIGBnZW9tX2RlbnNpdHkoKWAgZXN0aW1hIGxhIGZ1bmNpw7NuIGRlIGRlbnNpZGFkLCBwb3IgbG8gcXVlIGxhIGVzdGltYWNpw7NuIGRlcGVuZGUgZGUgdW5hIHNlcmllIGRlIHBhcmFtw6l0cm9zIGNvbW8gYGFkanVzdGAuIEhhY2Vtb3MgdXNvIGRlIGB4bGltKDMsIDkpYCBwYXJhIGV4cGFuZGlyIGxvc2zDrW1pdGVzIGRlbCBlamUgWC4KCgpgYGB7ciwgb3V0LndpZHRoID0gIjg1JSJ9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoKSkgKyAKICBnZW9tX2RlbnNpdHkoY29sb3IgPSAicmVkIiwgICBzaXplID0gMS4yKSArICAKICBnZW9tX2RlbnNpdHkoY29sb3IgPSAiYmx1ZSIsICBzaXplID0gMS4yLCBhZGp1c3QgPSAzKSArCiAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDEuMiwgYWRqdXN0ID0gMC41KSArICB4bGltKDIsIDEwKQpgYGAKCjxicj4KClBvZGVtb3MgYXNvY2lhciBsYSB2YXJpYWJsZSBTcGVjaWVzIGEgbGEgZXN0w6l0aWNhIGZpbGwgY29uIGBhZXMoZmlsbCA9IFNwZWNpZXMpYCBwYXJhIG9idGVuZXIgdW5hIGVzdGltYWNpw7NuIGRlIGxhIGRlbnNpZGFkIHBhcmEgY2FkYSB0aXBvIGRlIGxpcmlvLgoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgZmlsbCA9IFNwZWNpZXMpKSArIGdlb21fZGVuc2l0eShwb3NpdGlvbiA9ICJzdGFjayIsIGFscGhhID0gMC41KQpgYGAKCjxicj4KCkhheSBtdWNoYXMgb3RyYXMgcG9zaWJpbGlkYWRlcy4gUG9yIGVqZW1wbG86CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgZmlsbCA9IFNwZWNpZXMpKSArIGdlb21fZGVuc2l0eShwb3NpdGlvbiA9ICJmaWxsIiwgYWxwaGEgPSAwLjUpICMtIHBvc2l0aW9uID0gImZpbGwiCgpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgc3RhdChjb3VudCksIGZpbGwgPSBTcGVjaWVzKSkgKyBnZW9tX2RlbnNpdHkocG9zaXRpb24gPSAic3RhY2siLCBhbHBoYSA9IDAuNSkgIy0gc3RhdChjb3VudCkKYGBgCgoKTXVjaGFzIHZlY2VzIHNlIHN1ZWxlbiBoYWNlciBsb3MgaGlzdG9ncmFtYXMgc3VwZXJwb25pZW5kbyBsYSBmLiBkZSBkZW5zaWRhZCBub3JtYWwgbyB1bmEgZXN0aW1hY2nDs24gZGUgbGEgZGVuc2lkYWQgZGUgeCBjb24gYGdlb21fZGVuc2l0eSgpYCBvIGNvbiBgZ2VvbV9saW5lKHN0YXQ9ImRlbnNpdHkiKWAuIFBhcmEgcXVlIGVsIGdyw6FmaWNvIHNlIHZlYSBtZWpvciwgYWp1c3RhcmVtb3MgZWwgZWplIFggY29uIGAgeGxpbSgpYDoKCgpgYGB7cn0KIy0gY2FsY3VsYW1vcyBtZWRpYSB5IGRlc3ZpYWNpw7NuIHRpcGljYSBkZSBTZXBhbC5MZW5ndGggcGFyYSBsdWVnbyB1c2FybGFzIHBhcmEgY29uc3RydWlyIGxhIGN1cnZhIG5vcm1hbAptZWRpYSA8LSBtZWFuKGlyaXMkU2VwYWwuTGVuZ3RoLCBuYS5ybSA9IFRSVUUpICAgICAjLSBtZWRpYSBkZSBsYSBsb25naXR1ZCBkZWwgc8OpcGFsbwpkZXN2aWFjaW9uIDwtIHNkKGlyaXMkU2VwYWwuTGVuZ3RoLCBuYS5ybSA9IFRSVUUpICAjLSBkZXN2aWFjacOzbiAKCiMtIGhhY2Vtb3MgZWwgaGlzdG9ncmFtYQpwIDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoKSkgKwogICAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeT0uLmRlbnNpdHkuLiksICBjb2xvcj0iYmxhY2siLCBmaWxsID0gInN0ZWVsYmx1ZSIsIGFscGhhID0gMC4yKQoKIy0gbGUgYcOxYWRpbW9zIGxhIGRlbnNpaWRhZCBlc3RpbWFkYSB5IGxhIG5vcm1hbApwICsgZ2VvbV9kZW5zaXR5KCBjb2xvcj0icHVycGxlIiwgc2l6ZSA9IDEpICsKICAgIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGNvbG91ciA9ICJyZWQiLCBzaXplID0gMSwgYXJncyA9IGxpc3QobWVhbiA9IG1lZGlhLCBzZCA9IGRlc3ZpYWNpb24pKSAgKyAKICAgIHhsaW0oYyhtaW4oaXJpcyRTZXBhbC5MZW5ndGgpLTEsIDkpKQoKYGBgCgoKU2kgbmVjZXNpdGFzIHNhYmVyIG3DoXMgY29zYXMgc29icmUgbG9zIGhpc3RvZ3JhbWFzIHB1ZWRlcyBhY3VkaXIgW2FxdcOtXShodHRwczovL2VkYXYuaW5mby9oaXN0by5odG1sKSBvIFthcXXDrV0oaHR0cDovL3QtcmVkYWN0eWwuaW8vYmxvZy8yMDE2LzAyL2NyZWF0aW5nLXBsb3RzLWluLXItdXNpbmctZ2dwbG90Mi1wYXJ0LTctaGlzdG9ncmFtcy5odG1sKS4KCgoKIyMjIyBKb3kgRGl2aXNpb24gcGxvdHMKCkhhY2UgcG9jbyBhcGFyZWNpw7MgZWwgcGFxdWV0ZSBbZ2dyaWRnZXNdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ3JpZGdlcy92aWduZXR0ZXMvaW50cm9kdWN0aW9uLmh0bWwpIHkgc2UgcHVzaWVyb24gZGUgbW9kYSBlc3RvcyB0aXBvcyBkZSBncsOhZmljb3M6CgoKCmBgYHtyfQpsaWJyYXJ5KGdncmlkZ2VzKQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTZXBhbC5MZW5ndGgsIHkgPSBTcGVjaWVzKSkgKyBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFlcyhmaWxsID0gU3BlY2llcyksIGFscGhhID0gMC41KQpgYGAKCkNvbW8gc2UgcGFyZWNlbiBhIGxhIFttw610aWNhIHBvcnRhZGFdKGh0dHBzOi8vY29kaWdvZXNwYWd1ZXRpLmNvbS9ub3RpY2lhcy9jaWVuY2lhL2hpc3RvcmlhLXNlY3JldGEtdW5rbm93bi1wbGVhc3VyZXMvKSBkZWwgcHJpbWVyIGRpc2NvIGRlIEpveSBEaXZpc2lvbiwgYWxndW5vcyBsb3MgY29ub2NlbiBjb21vIEpveSBEaXZpc2lvbiBwbG90cy4gQWJham8gb3MgcG9uZ28gdW4gZWplbXBsbyBzYWNhZG8gZGUgbGEgW3ZpZ25ldHRlIGRlbCBwYXF1ZXRlIGdncmlkZ2VzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2dyaWRnZXMvdmlnbmV0dGVzL2ludHJvZHVjdGlvbi5odG1sKS4KCgpgYGB7cn0KbGlicmFyeSh2aXJpZGlzKQpnZ3Bsb3QobGluY29sbl93ZWF0aGVyLCBhZXMoeCA9IGBNZWFuIFRlbXBlcmF0dXJlIFtGXWAsIHkgPSBgTW9udGhgLCBmaWxsID0gLi54Li4pKSArCiAgZ2VvbV9kZW5zaXR5X3JpZGdlc19ncmFkaWVudChzY2FsZSA9IDMsIHJlbF9taW5faGVpZ2h0ID0gMC4wMSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhuYW1lID0gIlRlbXAuIFtGXSIsIG9wdGlvbiA9ICJDIikgKwogIGxhYnModGl0bGUgPSAnVGVtcGVyYXR1cmVzIGluIExpbmNvbG4gTkUgaW4gMjAxNicpCmBgYAoKCjxicj4KCiMjIyA2LjIgU2NhdHRlciBwbG90CgpTY2F0dGVyIHBsb3QsIGdyw6FmaWNvIGRlIHB1bnRvcyBvIGdyw6FmaWNvIFgtWS4gU2UgdXRpbGl6YSBwYXJhIG1vc3RyYXIgbGEgKipyZWxhY2nDs24gZW50cmUgRE9TIHZhcmlhYmxlcyBjb250aW51YXMqKi4gTG8gdGVuZW1vcyBtw6FzIHF1ZSB2aXN0bywgeWEgcXVlIGVzIGVsIHRpcG8gZGUgZ3LDoWZpY28gcXVlIGhlbW9zIHV0aWxpemFkbyBkdXJhbnRlIHRvZG8gZWwgdHV0b3JpYWwuIAoKYGBge3J9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykpICsgCiAgICAgZ2VvbV9wb2ludCgpICsKICAgICBsYWJzKHRpdGxlID0gIkdyw6FmaWNvIDE6IExvbmdpdHVkIGRlbCBzw6lwYWxvIGZyZW50ZSBhbCBww6l0YWxvIiwKICAgICAgIHN1YnRpdGxlID0gIihkaWZlcmVuY2lhbmRvIHBvciBlc3BlY2llIGRlIGxpcmlvKSIsCiAgICAgICBjYXB0aW9uID0gIkRhdG9zIHByb3ZlbmllbnRlcyBkZWwgSXJpcyBkYXRhc2V0IiwKICAgICAgIHggPSAiTG9uZ2l0dWQgZGVsIHPDqXBhbG8iLAogICAgICAgeSA9ICJMb25naXR1ZCBkZWwgcMOpdGFsbyIsCiAgICAgICBjb2xvciA9ICJFc3BlY2llIGRlIGxpcmlvIikKYGBgCgoKCiMjIyMgT3ZlcnBsb3R0aW5nCgpMb3MgZ3LDoWZpY29zIGRlIHB1bnRvcyBzZSB1c2FuIGhhYml0dWFsbWVudGUgcGFyYSBtb3N0cmFyIGxhIHJlbGFjacOzbiBlbnRyZSBkb3MgdmFyaWFibGVzIGNvbnRpbnVhcy4gRW4gY29uanVudG9zIGRlIGRhdG9zIGNvbiBtdWNoYXMgb2JzZXJ2YWNpb25lcyBwdWVkZW4gdGVuZXIgdW4gcHJvYmxlbWEgZGUgIm92ZXJwbG90dGluZyIuIEVzdGEgc2l0dWFjacOzbiBvY3VycmUgY3VhbmRvIHVub3MgcHVudG9zIHNlICBkaWJ1amFuIGVuY2ltYSBkZSBvdHJvcywgZGUgZm9ybWEgcXVlIG5vIHNlIGFwcmVjaWEgYmllbiBsYSByZWxhY2nDs24gZW50cmUgbGFzIHZhcmlhYmxlcwoKSGF5IHZhcmlhcyBmb3JtYXMgZGUgdHJhdGFyIGVzdGUgcHJvYmxlbWEuIFZlw6Ftb3NsbyBjb24gdW4gZWplbXBsbyBzYWNhZG8gZGUgbGEgd2ViIGRlIGdncGxvdDI6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpzZXQuc2VlZCgxMjM0KQpkZiA8LSBkYXRhLmZyYW1lKHggPSBybm9ybSgyMDAwKSwgeSA9IHJub3JtKDIwMDApKSAgICMtIGNyZWFtb3MgdW4gY29uanV0byBkZSBkYXRvcyBjb24gMjAwMCBvYnNlcnZhY2lvbmVzIHkgMiB2LgoKcCA8LSBnZ3Bsb3QoZGYsIGFlcyh4LCB5KSkgKyB4bGFiKE5VTEwpICsgeWxhYihOVUxMKQoKcCArIGdlb21fcG9pbnQoKQpwICsgZ2VvbV9wb2ludChzaGFwZSA9IDEpICMgSG9sbG93IGNpcmNsZXMKcCArIGdlb21fcG9pbnQoc2hhcGUgPSAiLiIpICMgUGl4ZWwgc2l6ZWQKCiMtIEZvciBsYXJnZXIgZGF0YXNldHMgd2l0aCBtb3JlIG92ZXJwbG90dGluZywgeW91IGNhbiB1c2UgYWxwaGEgYmxlbmRpbmcgKHRyYW5zcGFyZW5jeSkgdApwICsgZ2VvbV9wb2ludChhbHBoYSA9IDEgLyAxMCkKYGBgCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjg1JSJ9CnNldC5zZWVkKDEyMzQpCmRmIDwtIGRhdGEuZnJhbWUoeCA9IHJub3JtKDIwMDApLCB5ID0gcm5vcm0oMjAwMCkpICAgIy0gY3JlYW1vcyB1biBjb25qdXRvIGRlIGRhdG9zIGNvbiAyMDAwIG9ic2VydmFjaW9uZXMgeSAyIHYuCgpwIDwtIGdncGxvdChkZiwgYWVzKHgsIHkpKSArIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpCgpwMSA8LSBwICsgZ2VvbV9wb2ludCgpCnAyIDwtIHAgKyBnZW9tX3BvaW50KHNoYXBlID0gMSkgIyBIb2xsb3cgY2lyY2xlcwpwMyA8LSBwICsgZ2VvbV9wb2ludChzaGFwZSA9ICIuIikgIyBQaXhlbCBzaXplZAoKIy0gRm9yIGxhcmdlciBkYXRhc2V0cyB3aXRoIG1vcmUgb3ZlcnBsb3R0aW5nLCB5b3UgY2FuIHVzZSBhbHBoYSBibGVuZGluZyAodHJhbnNwYXJlbmN5KSB0CnA0IDwtIHAgKyBnZW9tX3BvaW50KGFscGhhID0gMSAvIDEwKQoKcDEgKyBwMiArIHAzICsgcDQgKyBwbG90X2xheW91dChuY29sID0gMikKYGBgCgoKPGJyPgoKCkRlIGZvcm1hIGFsdGVybmF0aXZhLCBwb2RlbW9zIGxpZGlhciBjb24gZWwgb3ZlcnBsb3R0aW5nIHV0aWxpemFuZG8gb3RybyBlbmZvcXVlIHF1ZSBjb25zaXN0ZSBlbiBwZW5zYXIgZW4gZWwgb3ZlcnBsb3R0aW5nIGNvbW8gdW4gcHJvYmxlbWEgZGUgZGVuc2lkYWQgZW4gMiBkaW1lbnNpb25lcyB5IHV0aWxpemFyIHVuIGhleC1wbG90LiBFbiB1biBoZXgtcGxvdCBzZSByZXByZXNlbnRhbiByZWdpb25lcyAoZ2VuZXJhbG1lbnRlIGhleGFnb25hbGVzKSBjb2xvcmVhZGFzIGVuIGZ1bmNpw7NuIGRlbCBuw7ptZXJvIGRlIG9ic2VydmFjaW9uZXMgcXVlIGNhZW4gZW4gY2FkYSByZWdpw7NuLCBkZSBmb3JtYSBxdWUgZXMgZXF1aXZhbGVudGUgYSB1biBoaXN0b2dyYW1hIHBlcm8gZW4gMmQuCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwICsgZ2VvbV9iaW4yZCgpCnAgKyBnZW9tX2JpbjJkKGJpbnMgPSAxMCkKcCArIGdlb21faGV4KCkKcCArIGdlb21faGV4KCkgKyBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAiIzEzMkI0MyIsIGhpZ2ggPSAiIzU2QjFGNyIpICAKYGBgCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjk1JSJ9CnAxIDwtIHAgKyBnZW9tX2JpbjJkKCkKcDIgPC0gcCArIGdlb21fYmluMmQoYmlucyA9IDEwKQpwMyA8LSBwICsgZ2VvbV9oZXgoKQpwNCA8LSBwICsgZ2VvbV9oZXgoKSArIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICIjMTMyQjQzIiwgaGlnaCA9ICIjNTZCMUY3IikgIApwMSArIHAyICsgcDMgKyBwNCArIHBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCgoKIAoKRW4gW2VzdGUgcG9zdF0oaHR0cHM6Ly9jdXIuYXQvbnVoWWtMMD9tPWVtYWlsJnNpZD05SXByVEN0KSB1dGlsaXphbiBoZXgtcGxvdHMgcGFyYSBtb3N0cmFyIGxhcyB6b25hcyBkZXNkZSBkb25kZSB0aXJhbiBhIGNhbmFzdGEgbG9zIGp1Z2Fkb3JlcyBOQkEuIEVuIFtlc3RlIG90cm8gcG9zdF0oaHR0cHM6Ly9yY3Jhc3RpbmF0ZS5yYmluZC5pby9wb3N0L3RoZXJlLWlzLWEtZ2FtZS1pLXBsYXktYW5hbHl6aW5nLW1ldGFjcml0aWMtc2NvcmVzLWZvci12aWRlby1nYW1lcy8pIHV0aWxpemFuIHVuYSBjb21iaW5hY2nDs24gZGUgYGdlb21fcG9pbnQoYWxwaGEgPSAuMiwgcGNoID0gMTUpICsgZ2VvbV9kZW5zaXR5XzJkKClgIHBhcmEgYW5hbGl6YXIgbGFzIHB1bnR1YWNpb25lcyBkZSB2aWRlb2p1ZWdvcy4gQWRlbcOhcyBoYWNlIHVuIHVzbyBhdmFuemFkbyBkZSBsYXMgZXNjYWxhcywgYWwgbWVub3MgcGFyYSBtaSwgcGFyYSBxdWUgZWwgZ3LDoWZpY28gc2VhIGbDoWNpbCBkZSB2aXN1YWxpemFyLgoKQWNhYmEgZGUgYXBhcmVjZXIgdW4gbnVldm8gZ2VvbTogW2BnZW9tX3BvaW50ZGVuc2l0eSgpYF0oaHR0cHM6Ly9naXRodWIuY29tL0xLcmVtZXIvZ2dwb2ludGRlbnNpdHkpLiBDb21vIGRpY2VuIGVuIHN1IHJlcG8gZGUgR2l0aHViOiAiQSBjcm9zcyBiZXR3ZWVuIGEgc2NhdHRlciBwbG90IGFuZCBhIDJEIGRlbnNpdHkgcGxvdCIuCgoKYGBge3J9CiNkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIkxLcmVtZXIvZ2dwb2ludGRlbnNpdHkiKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2dwb2ludGRlbnNpdHkpCnAgKyAgZ2VvbV9wb2ludGRlbnNpdHkoYWRqdXN0ID0gNykgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXMoKQoKYGBgCgoKU2kgcXVpZXJlcyBzYWJlciBtw6FzIGNvc2FzIHNvYnJlIGPDs21vIGhhY2VyIHNjYXR0ZXJwbG90cyBjb24gYGdncGxvdDJgIHB1ZWRlcyBoYWNlcmxvIHBvciBlamVtcGxvLCBbYXF1w61dKGh0dHA6Ly93d3cuY29va2Jvb2stci5jb20vR3JhcGhzL1NjYXR0ZXJwbG90c18oZ2dwbG90MikvKSBvIFthcXXDrV0oaHR0cHM6Ly9taWNoYWVsdG90aC5tZS9hLWRldGFpbGVkLWd1aWRlLXRvLXRoZS1nZ3Bsb3Qtc2NhdHRlci1wbG90LWluLXIuaHRtbCkuCgo8YnI+CgojIyMgNi4zIEJveHBsb3QgKGdyw6FmaWNvcyBkZSBjYWphKQoKUGFyYSB2aXN1YWxpemFyIHVuYSB2YXJpYWJsZSBjdWFudGl0YXRpdmEgc2Ugc3VlbGVuIHVzYXIgbG9zIGhpc3RvZ3JhbWFzLCBwZXJvIHNpIGxvIHF1ZSBxdWllcmVzIGVzIHZpc3VhbGl6YXIgY29tbyB2YXLDrWFuIGxvcyB2YWxvcmVzIGRlIHVuYSB2YXJpYWJsZSBjb250aW51YSBlbiBmdW5jacOzbiBkZSBsb3MgdmFsb3JlcyBkZSB1bmEgdmFyaWFibGUgY2F0ZWfDs3JpY2EgKFNwZWNpZXMpIHNlIHN1ZWxlbiB1c2FuIGJveHBsb3RzIG8gZGlhZ3JhbWFzIGRlIGNhamEuIFNvbiDDunRpbGVzIHBhcmEgY29tcGFyYXIgZGlmZXJlbnRlcyBncnVwb3MgeSBwYXJhIGlkZW50aWZpY2FyIG91dGxpZXJzLgoKClBhcmEgaGFjZXIgdW4gYm94cGxvdCBvIGRpYWdyYW1hIGRlIGNhamEgc2UgdXRpbGl6YSBgZ2VvbV9ib3hwbG90KClgLiBFbCBncsOhZmljbyBtdWVzdHJhIDUgZXN0YWTDrXN0aWNvczogbGEgbWVkaWFuYSBjb24gdW5hIGxpbmVhIGdydWVzYSwgZWwgcHJpbWVyIHkgdGVyY2VyIGN1YXJ0aWwgZGUgbG9zIGRhdG9zIGNvbiBsb3MgbGltaXRlcyBkZSBsYSBjYWphIHkgZWwgbcOheGltbyB5IGVsIG3DrW5pbW8gKGxvcyBsaW1pdGVzIGRlIGxhIGxpbmVhIHZlcnRpY2FsKS4gQWRpY2lvbmFsbWVudGUsIHNpIGV4aXN0ZW4gb3V0bGllcnMsIGVzdG9zIHRhbWJpw6luIHNlIHJlcHJlc2VudGFyw6FuLgoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTcGVjaWVzLCAgeSA9IFNlcGFsLkxlbmd0aCkpICsgZ2VvbV9ib3hwbG90KCkgCmBgYAoKU2UgcHVlZGVuIGNhbWJpYXIgYWxndW5hcyBvcGNpb25lcyBkZWwgZ3LDoWZpY28KCmBgYHtyfQpwIDwtIGdncGxvdChpcmlzLCBhZXMoeCA9IFNwZWNpZXMsICB5ID0gU2VwYWwuTGVuZ3RoKSkgCnAgKyBnZW9tX2JveHBsb3QoYWVzKGZpbGwgPSBTcGVjaWVzKSwgb3V0bGllci5jb2xvdXIgPSAicHVycGxlIikKYGBgCgoKQSB2ZWNlcywgcGFyYSBtZWpvcmFyIGxhIHZpc3VhbGl6YWNpw7NuLCBjb252aWVuZSByb3RhciBsb3MgZWplcy4gUG9kZW1vcyBoYWNlcmxvIGNvbiBgY29vcmRfZmxpcCgpYAoKYGBge3J9CnAgPC0gZ2dwbG90KGlyaXMsIGFlcyh4ID0gU3BlY2llcywgIHkgPSBTZXBhbC5MZW5ndGgpKSArIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IFNwZWNpZXMpKSAKcCArIGNvb3JkX2ZsaXAoKQpgYGAKCgpQYXJhIG1lam9yYXIgbGEgdmlzdWFsaXphY2nDs24gcG9kZW1vcyBpbmNsdWlyIGxhcyBvYnNlcnZhY2lvbmVzIG9yaWdpbmFsZXMgY29uIGBnZW9tX3BvaW50KClgLCBwZXJvIGhhYnLDrWEgbXVjaG8gb3ZlcnBsb3R0aW5nLCBhc8OtIHF1ZSBtZWpvciBjb24gYGdlb21faml0dGVyKClgCgoKYGBge3J9CnAgKyBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMTUsIGFscGhhID0gMS80LCBjb2xvciA9ICJ0b21hdG8iKQpgYGAKClBvZGVtb3MgYcOxYWRpciBjb24gYHN0YXRzKClgIGFsZ8O6biBlc3RhZMOtc3RpY28gbcOhcyBjb24gYHN0YXRzX3h4KClgLCBwb3IgZWplbXBsbyBsYSBtZWRpYSBjb24gYHN0YXRzX3N1bW1hcnlgOgoKCmBgYHtyfQpwICsgc3RhdF9zdW1tYXJ5KGZ1bi55ID0gIm1lYW4iLCBnZW9tID0gInBvaW50IiwgY29sb3IgPSAicHVycGxlIiwgc2l6ZSA9IDIuNSkKYGBgCgpMb3MgYm94cGxvdHMgcmVzdW1lbiBsYSBkaXN0cmlidWNpw7NuIGRlIHVuYSB2YXJpYWJsZSBjdWFudGl0YXRpdmEgY29uIHPDs2xvIGNpbmNvIG7Dum1lcm9zLCBwcm9wb3JjaW9uYW5kbyB1biByZXN1bWVuIMO6dGlsIGRlIGxvcyBkYXRvcywgcGVybyBvY3VsdGFuIGxhIGZvcm1hIGRlIGxhIGRpc3RyaWJ1Y2nDs247IHBvciBlamVtcGxvLCBzaSBsYSBkaXN0cmlidWNpw7NuIGZ1ZXNlIGJpbW9kYWwsIG5vIGxvIGFwcmVjaWFyw61hbW9zLiBBZGVtw6FzLCBhdW5xdWUgdXNlbW9zIGBnZW9tX2ppdHRlcigpYCBwYXJhIHN1cGVycG9uZXIgbG9zIHZhbG9yZXMgb3JpZ2luYWxlcywgYSB2ZWNlcyAgZXMgZGlmw61jaWwgYSBzaW1wbGUgdmlzdGEgaW5mZXJpciBsYSBkaXN0cmlidWNpw7NuIGRlIGVzdG9zLiBQb3IgZWxsbyBoYXkgdmFyaWFzIHTDqWNuaWNhcy9nZW9tcyDDunRpbGVzIHF1ZSBheXVkYW4gYSByZXNvbHZlciBlbCBwcm9ibGVtYSwgcG9yIGVqZW1wbG8gYGdlb21fdmlvbGluKClgLiBVbmEgYWx0ZXJuYXRpdmEgYWwgYm94cGxvdCBzb24gbG9zIGdyw6FmaWNvcyBkZSB2aW9sw61uIGRvbmRlIHNlIGVzdGltYSB5IG11ZXN0cmEgbGEgZm9ybWEgZGUgbGEgZGlzdHJpYnVjacOzbiBkZSBsYXMgb2JzZXJ2YWNpb25lczoKCmBgYHtyLCBldmFsID0gRkFMU0V9CnAgPC0gZ2dwbG90KGlyaXMsIGFlcyh4ID0gU3BlY2llcywgIHkgPSBTZXBhbC5MZW5ndGgpKSAKcCArIGdlb21fdmlvbGluKGFlcyhmaWxsID0gU3BlY2llcyksIGFscGhhID0gMC42KQpwICsgZ2VvbV92aW9saW4oYWVzKGZpbGwgPSBTcGVjaWVzKSwgYWxwaGEgPSAwLjYpICsgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjE1LCBhbHBoYSA9IDEvNCkKYGBgCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjgwJSJ9CnAgPC0gZ2dwbG90KGlyaXMsIGFlcyh4ID0gU3BlY2llcywgIHkgPSBTZXBhbC5MZW5ndGgpKSAKcDEgPC0gcCArIGdlb21fdmlvbGluKGFlcyhmaWxsID0gU3BlY2llcyksIGFscGhhID0gMC42KQpwMiA8LSBwICsgZ2VvbV92aW9saW4oYWVzKGZpbGwgPSBTcGVjaWVzKSwgYWxwaGEgPSAwLjYpICsgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjE1LCBhbHBoYSA9IDEvNCkKcDEgKyBwMiArIHBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCjxicj4KCkVuIGVsIGVqZW1wbG8gcXVlIGhlbW9zIHVzYWRvIGhlbW9zIHRlbmlkbyBzdWVydGUgeSBsb3MgdHJlcyBncnVwb3MgZXN0YWJhbiBvcmRlbmFkb3MsIHBlcm8gb3RyYXMgdmVjZXMgbm8gdGVuZHJlbW9zIHRhbnRhIHN1ZXJ0ZSwgcG9yIGVqZW1wbG8gc2kgZW4gbHVnYXIgZGUgZ3JhZmljYXIgbGEgbG9uZ2l0dWQgZGUgc8OpcGFsbyBxdWVyZW1vcyB2aXN1YWxpemFyIGxhIGFuY2h1cmEgKFNlcGFsLldpZHRoKToKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTcGVjaWVzLCAgeSA9IFNlcGFsLldpZHRoKSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCsK/Q8OzbW8gb3JkZW5hbW9zIGxvcyBncnVwb3M/IFBvciBlamVtcGxvIHBvZGVtb3Mgb3JkZW5hcmxvcyBkZSBtZW5vciBhIG1heW9yIGVuIGZ1bmNpw7NuIGRlIHN1IGFuY2h1cmEgbWVkaWEgZGVsIHPDqXBhbG8uIFBhcmEgZWxsbyB1c2Ftb3MgYHN0YXRzOjpyZW9yZGVyKClgCgoKYGBge3J9CmdncGxvdChpcmlzLCBhZXMoeCA9IHJlb3JkZXIoU3BlY2llcywgU2VwYWwuV2lkdGgsIG1lYW4pLCAgeSA9IFNlcGFsLldpZHRoKSkgKyBnZW9tX2JveHBsb3QoKSArCiAgeGxhYigiRGUgbWVub3IgYSBtYXlvciBhbmNodXJhIGRlbCBzw6lwYWxvIikKYGBgCgoKCgoKICAKIyMjIDYuNCBHcsOhZmljb3MgZGUgYmFycmFzCgpMb3MgZ3LDoWZpY29zIGRlIGJhcnJhcyBzZSB1dGlsaXphbiBwYXJhIHJlcHJlc2VudGFyIHVuYSB2YXJpYWJsZSBjYXRlZ8OzcmljYSwgY29tbyBwb3IgZWplbXBsbyBTcGVjaWVzLCBvIHZhcmlhYmxlcyBjdWFudGl0YXRpdmFzIGRpc2NyZXRhcy4gU2UgcmVwcmVzZW50YW4gYmFycmFzIHZlcnRpY2FsZXMgcHJvcG9yY2lvbmFsZXMgYSBsb3MgdmFsb3JlcyBkZSBsYSB2YXJpYWJsZSBlbiBjYWRhIGNhdGVnb3LDrWEgbyB2YWxvci4gUGFyYSBjcmVhciBncsOhZmljb3MgZGUgYmFycmFzIGNvbiBnZ3Bsb3QyIHNlIHVzYSBgZ2VvbV9iYXIoKWAuCgpQYXJhIGhhY2VyIG51ZXN0cm8gcHJpbWVyIGdyw6FmaWNvIGRlIGJhcnJhcyBubyB2YW1vcyBhIHV0aWxpemFyIGBpcmlzYCBwb3JxdWUgZW4gbGEgw7puaWNhIHZhcmlhYmxlIGNhdGVnw7NyaWNhIChTcGVjaWVzKSByZXN1bHRhIHF1ZSBoYXkgNTAgbGlyaW9zIGVuIGNhZGEgdGlwbyBkZSBlc3BlY2llLiBVc2FyZW1vcyBlbCBkYXRhLmZyYW1lIGBtcGdgIGRlbCBwYXF1ZXRlIGdncGxvdDIgcXVlIGNvbnRpZW5lIDIzNCBvYnNlcnZhY2lvbmVzIHNvYnJlIGRpc3RpbnRvcyBtb2RlbG9zIGRlIGNvY2hlcyB5IHN1cyBjYXJhY3RlcsOtc3RpY2FzLiBBbGd1bm9zIGRlIGxvcyBlamVtcGxvcyBlc3TDoW4gc2FjYWRvcyBkZSBsYSBbd2ViIGRlIGdncGxvdDJdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZW9tX2Jhci5odG1sKS4KCmBgYHtyLCBldmFsID0gRkFMU0V9CnAgPC0gZ2dwbG90KG1wZywgYWVzKGNsYXNzKSkKcCArIGdlb21fYmFyKCkKcCArIGdlb21fYmFyKGZpbGwgPSAic3RlZWxibHVlIikgKyBjb29yZF9mbGlwKCkKYGBgCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjk1JSIsIGZpZy5hc3AgPSA2Lzl9CnAgPC0gZ2dwbG90KG1wZywgYWVzKGNsYXNzKSkKcDEgPC0gcCArIGdlb21fYmFyKCkKcDIgPC0gcCArIGdlb21fYmFyKGZpbGwgPSAic3RlZWxibHVlIikgKyBjb29yZF9mbGlwKCkKcDEgKyBwMiArIHBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCgpDb21vIHZlbW9zLCBsYSB2YXJpYWJsZSBgY2xhc3NgIGVzIGNhdGVnw7NyaWNhLCBjb25jcmV0YW1lbnRlIHRpZW5lIDcgZ3J1cG9zIG8gY2F0ZWdvcsOtYXMgZGUgY29jaGVzLiBMYXMgYmFycmFzIHZlcnRpY2FsZXMgc29uIHByb3BvcmNpb25hbGVzIGFsIG7Dum1lcm8gZGUgdmVow61jdWxvcyBlbiBjYWRhIGNhdGVnb3LDrWEuIENvbiBgY29vcmRfZmxpcCgpYCBsYXMgY2F0ZWdvcsOtYXMgcGFzYW4gYSByZXByZXNlbnRhcnNlIGVuIGVsIGVqZSBZLCBoYWNpZW5kbyBxdWUsIGdlbmVyYWxtZW50ZSwgc2UgdmlzdWFsaWNlbiBtZWpvciBsb3Mgbm9tYnJlcyBkZSBsYXMgY2F0ZWdvcsOtYXMuCgoKCjxicj4KClNpIGVuIGx1Z2FyIGRlIHRlbmVyIHVuYSB0YWJsYSBkZSBkYXRvcywgdGVuZW1vcyB5YSB1bmEgKip0YWJsYSBkZSBmcmVjdWVuY2lhcyoqLCB0ZW5kcmVtb3MgcXVlIGVzcGVjaWZpY2FyIGVuIGBhZXMoKWAgcXVlIGxhIHZhcmlhYmxlIGNvbiBsYXMgZnJlY3VlbmNpYXMgc2UgbWFwZWUvYXNvY2llIGFsIGVqZSBZOiBgYWVzKHggPSB2YXJpYWJsZSwgeSA9IGZyZWN1ZW5jaWFzKWAuIEFkZW3DoXMgdGVuZHLDoXMgcXVlIHVzYXIgYGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKWAgbyBkaXJlY3RhbWVudGUgdXNhciBgZ2VvbV9jb2woKWAgcXVlIHlhIHVzYSBwb3IgZGVmZWN0byBzdGF0X2lkZW50aXR5KCkuIFZlw6Ftb3NsbyBjb24gdW4gZWplbXBsbzoKCgpgYGB7cn0KZGYgPC0gbXBnICU+JSBncm91cF9ieShjbGFzcykgJT4lIGNvdW50CnAgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IGNsYXNzLCB5ID0gbikpCnAgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgIGZpbGwgPSAic3RlZWxibHVlIikgCiMgcCArIGdlb21fY29sKGZpbGwgPSAic3RlZWxibHVlIikgICAgICAgICAgICAgICAgICAgICAgICAgICMtIGhhY2UgZXhhY3RhbWVudGUgZWwgbWlzbW8gcGxvdApgYGAKCjxicj4KCiMjIyMgRGlzdGludGFzIHBvc2ljaW9uZXMgcGFyYSBsYXMgYmFycmFzCgpQYXJhIGVzdGEgc3Vic2VjY2nDs24gdXRpbGl6YXJlbW9zIGVsIGRhdGEuZnJhbWUgYG10Y2Fyc2AKCmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChtdGNhcnMsIGFlcyhmYWN0b3IoY3lsKSwgZmlsbCA9IGZhY3Rvcih2cykpKSArIGdlb21fYmFyKCkgIy0gcG9zCmdncGxvdChtdGNhcnMsIGFlcyhmYWN0b3IoY3lsKSwgZmlsbCA9IGZhY3Rvcih2cykpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKQpnZ3Bsb3QobXRjYXJzLCBhZXMoZmFjdG9yKGN5bCksIGZpbGwgPSBmYWN0b3IodnMpKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIpCmdncGxvdChtdGNhcnMsIGFlcyhmYWN0b3IoY3lsKSwgZmlsbCA9IGZhY3Rvcih2cykpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UyKHByZXNlcnZlID0gInNpbmdsZSIpKQpgYGAKCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiMTAwJSIsICBmaWcuYXNwID0gNS85fQpwMSA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoZmFjdG9yKGN5bCksIGZpbGwgPSBmYWN0b3IodnMpKSkgKyBnZW9tX2JhcigpICMtIHBvcwpwMiA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoZmFjdG9yKGN5bCksIGZpbGwgPSBmYWN0b3IodnMpKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikKcDMgPC0gZ2dwbG90KG10Y2FycywgYWVzKGZhY3RvcihjeWwpLCBmaWxsID0gZmFjdG9yKHZzKSkpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKQpwNCA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoZmFjdG9yKGN5bCksIGZpbGwgPSBmYWN0b3IodnMpKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHByZXNlcnZlID0gInNpbmdsZSIpKQoKcDEgKyBwMiArIHAzICsgcDQgKyBwbG90X2xheW91dChuY29sID0gMikKYGBgCgoKPGJyPgoKIyMjIyBSZW9yZGVuYW5kbyBsYXMgY2F0ZWdvcsOtYXMKCkEgdmVjZXMgZXMgaW1wb3J0YW50ZSByZW9yZGVuYXIgbGFzIGJhcnJhcyBwYXJhIG1lam9yYXIgbGEgdmlzdWFsaXphY2nDs24uIFBhcmEgZWxsbyB0ZW5kcmVtb3MgcXVlIHVzYXIgKipmYWN0b3JlcyoqLiBQb3IgZWplbXBsbywgdm9sdmllbmRvIGFsIGBtcGdgIGRhdGFzZXQ6IAoKCmBgYHtyfQpkZiA8LSBtcGcKZGYgPC0gZGYgJT4lIG11dGF0ZShjbGFzcyA9IGZvcmNhdHM6OmFzX2ZhY3RvcihjbGFzcykpICMtIGNvbnZlcnRpbW9zIGxhIHYuIGNsYXNzIGEgZmFjdG9yIGNvbiBsYSBmLiBhc19mYWN0b3IoKQpkZiA8LSBkZiAlPiUgbXV0YXRlKGNsYXNzID0gZm9yY2F0czo6ZmN0X2luZnJlcShjbGFzcykpICMtIGZjdF9pbmZyZXEoKSBsb3Mgbml2ZWxlcyBkZWwgZmFjdG9yIHNlZ8O6biBzdSBmcmVjdWVuY2lhIGRlIG1heW9yIGEgbWVub3IKcCA8LSBnZ3Bsb3QoZGYsIGFlcyhmY3RfcmV2KGNsYXNzKSkpICMtIGZjdF9yZXYoKSBvcmRlbmEgbG9zIGxldmVscyBkZSBtZW5vciBhIG1heW9yCnAgKyBnZW9tX2JhcihmaWxsID0gInN0ZWVsYmx1ZSIpICsgY29vcmRfZmxpcCgpCmBgYAoKPGJyPgoKU2kgcXVlcmVtb3MgcXVlIGVuIGxhcyBiYXJyYXMgc2UgdmlzdWFsaWNlIGVsIG7Dum1lcm8gZGUgb2JzZXJ2YWNpb25lcyBvIGZyZWN1ZW5jaWEgYWJzb2x1dGEgZGUgY2FkYSBjYXRlZ29yw61hOgoKCmBgYHtyfQpwICsgZ2VvbV9iYXIoZmlsbCA9ICJzdGVlbGJsdWUiKSArIGNvb3JkX2ZsaXAoKSArCiAgICBnZW9tX3RleHQoc3RhdD0nY291bnQnLCBhZXMobGFiZWwgPSAuLmNvdW50Li4gKSwgaGp1c3QgPSAtMC4xNSwgc2l6ZSA9IDMuMjUpIApgYGAKCgoKPGJyPgoKIyMjIyBQb3JjZW50YWplcyBlbiBsdWdhciBkZSBjb3VudHMKIApTaSBxdWVyZW1vcyBxdWUgbGFzIGJhcnJhcyByZXByZXNlbnRlbiBwb3JjZW50YWplcyBlbiBsdWdhciBkZSBudW1lcm8gZGUgY2Fzb3MgZW4gY2FkYSBjYXRlZ29yw61hOgoKCmBgYHtyfQpwIDwtIGdncGxvdChkZiwgYWVzKGZjdF9yZXYoY2xhc3MpKSkgIy0gZmN0X3JldigpIG9yZGVuYSBsb3MgbGV2ZWxzIGRlIG1lbm9yIGEgbWF5b3IKcCArIGdlb21fYmFyKGFlcyggeSA9ICguLmNvdW50Li4pL3N1bSguLmNvdW50Li4pKSwgZmlsbCA9ICJzdGVlbGJsdWUiKSAgKyBjb29yZF9mbGlwKCkKYGBgCgoKRW4gcmVhbGlkYWQgZXN0byBtaXNtbyBzZSBwdWRlIGhhY2VyIGRlIHZhcmlhcyBmb3JtYXMuIEVuIFtlc3RlIHBvc3RdKGh0dHBzOi8vc2ViYXN0aWFuc2F1ZXIuZ2l0aHViLmlvL3BlcmNlbnRhZ2VfcGxvdF9nZ3Bsb3QyX1YyLykgbm9zIG11ZXN0cmFuIDUgZm9ybWFzIGRlIGhhY2VybG8uCgoKPGJyPgogU2kgcXVpZXJlcyBzYWJlciBtw6FzIHNvYnJlIGNvbW8gaGFjZXIgZ3LDoWZpY29zIGRlIGJhcnJhcyBjb24gZ2dwbG90MiwgcHVlZGVzIGhhY2VybG8gW2FxdcOtXSgKaHR0cDovL3d3dy5zdGhkYS5jb20vZW5nbGlzaC93aWtpL2dncGxvdDItYmFycGxvdHMtcXVpY2stc3RhcnQtZ3VpZGUtci1zb2Z0d2FyZS1hbmQtZGF0YS12aXN1YWxpemF0aW9uCikgbyBbYXF1w61dKGh0dHBzOi8vd3d3LnItZ3JhcGgtZ2FsbGVyeS5jb20vNDgtZ3JvdXBlZC1iYXJwbG90LXdpdGgtZ2dwbG90Mi5odG1sKS4KCgo8YnI+CgoKIyMjIDYuNSBHcsOhZmljb3MgZGUgbGluZWFzCgpMb3MgZ3LDoWZpY29zIGRlIGxpbmVhcyBzZSB1c2FuIHByaW5jaXBhbG1lbnRlIHBhcmEgbW9zdHJhciBsYSBldm9sdWNpw7NuIGRlIHZhcmlhYmxlcyBlbiBlbCB0aWVtcG8uIEdlbmVyYWxtZW50ZSBsYSB2YXJpYWJsZSBxdWUgc2UgbXVlc3RyYSBlbiBlbCBlamUgWCBlcyBlbCB0aWVtcG8uIEVuIEVjb25vbcOtYSBlc3RvcyBncsOhZmljb3MgcHVlZGUgcXVlIHNlYW4gbG9zIG3DoXMgdXNhZG9zIHkgaGFiaXR1YWxtZW50ZSBub3MgcmVmZXJpbW9zIGEgZWxsb3MgY29tbyBncsOhZmljb3MgdGVtcG9yYWxlcy4KCgpQb2RlbW9zIHNpbXVsYXIgZ3LDoWZpY29zIGRlIHRpZW1wbyBjb24gYGdlb21fbGluZSgpYCB5IGBnZW9tX3BhdGgoKWAuIEVzdG9zIDIgZ2VvbXMgZ3JhZmljYW4gbGluZWFzIGVudHJlIGRvcyBvYnNlcnZhY2lvbmVzIGRlIHVuYSB2YXJpYWJsZS4gUG9yIGVqZW1wbG86CgoKYGBge3J9CmdncGxvdChlY29ub21pY3MsIGFlcyhkYXRlLCB1ZW1wbWVkKSkgKyBnZW9tX2xpbmUoKQpgYGAKCgpFbXBlY2Vtb3MgdXNhbmRvIGVsIGNvbmp1bnRvIGRlIGRhdG9zIGBnYXBtaW5kZXJgIHBhcmEgbW9zdHJhciBsYXMgb2JzZXJ2YWNpb25lcyBkZSBsYSB2YXJpYWJsZSBsaWZlRXhwIHBhcmEgRXNwYcOxYToKCmBgYHtyfQpsaWJyYXJ5KGdhcG1pbmRlcikKZ2FwbWluZGVyICU+JSBmaWx0ZXIoY291bnRyeSA9PSAiU3BhaW4iKSAlPiUgCmdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBsaWZlRXhwKSkgKyBnZW9tX2xpbmUoKSArIGdlb21fcG9pbnQoKQpgYGAKCgpWaXN1YWxpY2Vtb3MgYWhvcmEgNCBzZXJpZXMgZGUgdGllbXBvLCBsYSBlc3BlcmFuemEgZGUgdmlkYSBlbiBjdWF0cm8gcGHDrXNlcyBldXJvcGVvcy4gUGFyYSBwb2RlciBkaXN0aW5ndWlyIGxhcyBsaW5lYXMgZGUgY2FkYSBwYcOtcyBwb2RlbW9zIGFzb2NpYXIgbGEgdmFyaWFibGUgY291bnRyeSBhIGxhIGNhcmFjdGVyw61zdGljYSBlc3TDqXRpY2EgY29sb3I6IGBhZXMoY29sb3IgPSBjb3VudHJ5KWAKCmBgYHtyfQpnYXBtaW5kZXIgJT4lIGZpbHRlcihjb3VudHJ5ICVpbiUgYygiU3BhaW4iLCAiRnJhbmNlIiwgIk5vcndheSIsICJCZWxnaXVtIikpICU+JSAKZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IGxpZmVFeHAsIGNvbG9yID0gY291bnRyeSkpICsgZ2VvbV9saW5lKCkgKyBnZW9tX3BvaW50KCkKYGBgCgpVbmEgdG9udGVyw61hIHBlcm8gcXVlIHB1ZWRlIHNlciBkZSB1dGlsaWRhZCBwYXJhIHNhYmVyIGN1YW50YSBkaWZlcmVuY2lhIGhheSBhbCBmaW5hbCBkZWwgcGxvdC4gTG8gYXByZW5kw60gW2FxdcOtXShodHRwczovL2Ryc2ltb25qLnN2YnRsZS5jb20vbGFiZWwtbGluZS1lbmRzLWluLXRpbWUtc2VyaWVzLXdpdGgtZ2dwbG90MikKCgpgYGB7cn0KZGYgPC0gZ2FwbWluZGVyICU+JSBmaWx0ZXIoY291bnRyeSAlaW4lIGMoIlNwYWluIiwgIkZyYW5jZSIsICJOb3J3YXkiLCAiQmVsZ2l1bSIpKSAKbGlmZUV4cF9lbmRzIDwtIGRmICU+JSBncm91cF9ieShjb3VudHJ5KSAlPiUgdG9wX24oMSwgeWVhcikgJT4lIHB1bGwobGlmZUV4cCkgIy0gdmVjdG9yIGNvbiBsb3MgdmFsb3JlcyDDumx0aW1vcyBkZSBsaWZlRXhwCmdncGxvdChkZiwgYWVzKHggPSB5ZWFyLCB5ID0gbGlmZUV4cCwgY29sb3IgPSBjb3VudHJ5KSkgKyBnZW9tX2xpbmUoKSArIAogICAgIHNjYWxlX3lfY29udGludW91cyhzZWMuYXhpcyA9IHNlY19heGlzKH4gLiwgYnJlYWtzID0gbGlmZUV4cF9lbmRzKSkgKyAgICMtIHNlY19heGlzKCkgZXNwZWNpZmljYSB1biBlamUgc2VjdW5kYXJpbwogICAgIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApKQpgYGAgCgo8YnI+CgpFbCBzaWd1aWVudGUgZWplbXBsbyBlc3TDoSBzYWNhZG8gZGUgW2VzdGUgbGlicm9dKGh0dHBzOi8vZWRhdi5pbmZvL3RpbWVzZXJpZXNiYXNpYy5odG1sKSBhw7puIGVuIGNvbnN0cnVjY2nDs24uIFV0aWxpemEgZWwgcGFxdWV0ZSBbdGlkeXF1YW50XShodHRwczovL2dpdGh1Yi5jb20vYnVzaW5lc3Mtc2NpZW5jZS90aWR5cXVhbnQpIHBhcmEgZGVzY2FyZ2FyIGxhcyBjb3RpemFjaW9uZXMgZGUgbGFzIDQgcHJpbmNpcGFsZXMgZW1wcmVzYXMgdGVjbm9sw7NnaWNhcywgbGFzIEdBRkEuIGB0aWR5cXVhbnRgIHRpZW5lIG11Y2hhcyBmdW5jaW9uZXMgaW50ZXJlc2FudGVzLCBwdWVkZXMgdmVybGFzIGNvcnJpZW5kbyBgdHFfdHJhbnNtdXRlX2Z1bl9vcHRpb25zKClgLgoKYGBge3J9CmxpYnJhcnkodGlkeXF1YW50KQpzdG9ja3MgPC0gYygiR09PR0wiLCJBTVpOIiwiRkIiLCJBQVBMIikgIy0gc2VsZWNjaW9uYW1vcyBhIGxhcyBHQUZBcwpkZiA8LSB0cV9nZXQoc3RvY2tzLCBmcm9tID0gYXMuRGF0ZSgiMjAxMy0wMS0wMSIpLCB0byA9IGFzLkRhdGUoIjIwMTMtMTItMzEiKSkKCmdncGxvdChkZiwgYWVzKGRhdGUsIHkgPSBjbG9zZSwgY29sb3IgPSBmY3RfcmVvcmRlcjIoc3ltYm9sLCBkYXRlLCBjbG9zZSkpKSArCiAgZ2VvbV9saW5lKCkgKyB4bGFiKCIiKSArIHlsYWIoIiIpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKUmVlc2NhbGFtb3MgcGFyYSBxdWUgbGFzIHNlcmllcyBjb21pZW5jZW4gdG9kYXMgZW4gMTAwCgpgYGB7cn0KZGYgPC0gZGYgJT4lIGdyb3VwX2J5KHN5bWJvbCkgJT4lIG11dGF0ZShyZXNjYWxlZF9jbG9zZSA9IDEwMCpjbG9zZSAvIGNsb3NlWzFdKQoKZ2dwbG90KGRmLCBhZXMoZGF0ZSwgeSA9IHJlc2NhbGVkX2Nsb3NlLCBjb2xvciA9IGZjdF9yZW9yZGVyMihzeW1ib2wsIGRhdGUsIHJlc2NhbGVkX2Nsb3NlKSkpICsKICBnZW9tX2xpbmUoKSArIHhsYWIoIiIpICsgeWxhYigiIikgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgo8YnI+CgpPdHJvIHBhcXVldGUgbXV5IMO6dGlsIHBhcmEgYW5hbGl6YXIgeSBkZXNjYXJnYXIgZGF0b3MgZmluYW5jaWVyb3MgZXMgW2BxdWFudG1vZGBdKGh0dHA6Ly93d3cucXVhbnRtb2QuY29tLykuIEVsIGVqZW1wbG8gc2lndWllbnRlIGxvIGhlIHNhY2FkbyBkZSBbZXN0ZSBmYW50YXN0aWNvIGxpYnJvXShodHRwczovL3NtYWMtZ3JvdXAuZ2l0aHViLmlvL2RzL2RhdGEuaHRtbCNleGFtcGxlLWFwcGxlLXN0b2NrLXByaWNlKS4KCgpgYGB7cn0KbGlicmFyeShxdWFudG1vZCkKdG9kYXkgPC0gU3lzLkRhdGUoKQp0aHJlZV9tb250aHNfYWdvIDwtIHNlcSh0b2RheSwgbGVuZ3RoID0gMiwgYnkgPSAiLTMgbW9udGhzIilbMl0KZ2V0U3ltYm9scygiQUFQTCIsIGZyb20gPSB0aHJlZV9tb250aHNfYWdvLCB0byA9IHRvZGF5KQpjYW5kbGVDaGFydChBQVBMLCB0aGVtZSA9ICd3aGl0ZScsIHR5cGUgPSAnY2FuZGxlcycpCmBgYAoKPGJyPgoKU2kgcXVpZXJlcyBzYWJlciBtw6FzIHNvYnJlIGdyw6FmaWNvcyBkZSBsaW5lYXMgcHVlZGVzIGlyIFthcXXDrV0oaHR0cHM6Ly9taWNoYWVsdG90aC5tZS9hLWRldGFpbGVkLWd1aWRlLXRvLXBsb3R0aW5nLWxpbmUtZ3JhcGhzLWluLXItdXNpbmctZ2dwbG90LWdlb21fbGluZS5odG1sKS4KCgojIyMjIyBVbiBwb2NvIHNvYnJlIGRhdG9zIHRlbXBvcmFsZXMKClBhcmEgdHJhYmFqYXIgY29uIGRhdG9zIHRlbXBvcmFsZXMsIFIgdGllbmUgZGlzdGludG9zIHBhcXVldGVzIHkgZXN0cnVjdHVyYXMsIHBlcm8gcGFyYSBtYW5pcHVsYXIgZmVjaGFzIHkgIGRhdG9zIHRlbXBvcmFsZXMgZW4gZWwgdGlkeXZlcnNlIHRlbmVtb3MgZWwgcGFxdWV0ZSBbYGx1YnJpZGF0ZWBdKGh0dHBzOi8vbHVicmlkYXRlLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2x1YnJpZGF0ZS1wYWNrYWdlLmh0bWwpLiBQYXJhIHVuYSBpbnRyb2R1Y2Npw7NuIGEgZmVjaGFzIHkgdGllbXBvIGVuIFIgW2FxdcOtXShodHRwczovL2RlcmVrc29uZGVyZWdnZXIuZ2l0aHViLmlvLzU3MEwvMTQtZGF0ZXMtYW5kLXRpbWVzLmh0bWwpLgoKCkVuIGdlbmVyYWwsIHBhcmEgcmVhbGl6YXIgYW7DoWxpc2lzIGVzdGFkw61zdGljb3MgZW4gUiBjb24gc2VyaWVzIGRlIHRpZW1wbyBzZSBuZWNlc2l0YSBxdWUgbG9zIGRhdG9zIGVzdMOpbiBlbiBtYXRyaWNlcywgcGVybyBub3NvdHJvcyBlbiBlbCBjdXJzbyBlc3RhbW9zIHRyYWJhamFuZG8gY29uIGVsIHRpZHl2ZXJzZSwgeSBsYSBsYSBlc3RydWN0dXJhIGRlIGRhdG9zIHVzYWRhIHNvbiBsb3MgZGF0YWZyYW1lcyBvIHRpYmJsZXMsIGVzdG8gZXJhIHVuIHByb2JsZW1hLCBwZXJvIHJlY2llbnRlbWVudGUsIGVsIHBhcXVldGUgW2B0c2liYmxlYF0oaHR0cHM6Ly90c2liYmxlLnRpZHl2ZXJ0cy5vcmcvKSBoYSBleHRlbmRpZG8gZWwgdGlkeXZlcnNlIHkgbGFzIHRpYmJsZXMgYSBsb3MgZGF0b3MgdGVtcG9yYWxlcywgY3JlYW5kbyB1bmEgbnVldmEgZXN0cnVjdHVyYSBkZSBkYXRvczogbGFzICJ0c2liYmxlcyIuIAoKQXBvecOhbmRvc2UgZW4gZXN0YSBudWV2YSBlc3RydWN0dXJhIGRlIGRhdG9zLCBsYXMgInRzaWJibGVzIiwgZWwgcGFxdWV0ZSBbYGZlYXN0YF0oaHR0cDovL2ZlYXN0cy50aWR5dmVydHMub3JnLykgcHJvcG9yY2lvbmEgbGFzIGhlcnJhbWllbnRhcyB5IGZ1bmNpb25lcyBuZWNlc2FyaWFzIHBhcmEgdHJhYmFqYXIgY29uIHNlcmllcyB0ZW1wb3JhbGVzIGVuIHVuIGVudG9ybm8gdGlkeS4gYGZlYXN0YCBlcyB1biBhY3LDs25pbW8gZGUgRmVhdHVyZSBFeHRyYWN0aW9uIEFuZCBTdGF0aXN0aWNzIGZvciBUaW1lIFNlcmllcy4gUGFyYSB1bmEgaW50cm9kdWNjacOzbiBhIGBmZWFzdGAgcHVlZGVzIGlyIFthcXXDrV0oaHR0cHM6Ly9ibG9nLm1pdGNoZWxsb2hhcmF3aWxkLmNvbS9ibG9nL2ZlYXN0cy8pIG8gW2FxdcOtXShodHRwczovL3JvYmpoeW5kbWFuLmNvbS9oeW5kc2lnaHQvZmVhc3RzLykuIAoKQXNpbWlzbW8sIGVsIHBhcXVldGUgW2BmYWJsZWBdKGh0dHA6Ly9mYWJsZS50aWR5dmVydHMub3JnLykgdGFtYmnDqW4gdXRpbGl6YSAidHNpYmJsZXMiIHkgcHJvcG9yY2lvbmEgIGhlcnJhbWllbnRhcyBwYXJhIGxhIHByZWRpY2Npw7NuIGRlIHNlcmllcyB0ZW1wb3JhbGVzLCBpbmNsdXllbmRvIG1vZGVsb3MgQVJJTUEgZW4gZWwgZW50b3JubyB0aWR5dmVyc2UuIFBhcmEgdW5hIGludHJvZHVjY2nDs24gYSBgZmFibGVgIHB1ZWRlcyBpciBbYXF1w61dKGh0dHBzOi8vYmxvZy5taXRjaGVsbG9oYXJhd2lsZC5jb20vYmxvZy9mYWJsZS8pLgoKW0FxdcOtXShodHRwczovL3Jlc291cmNlcy5yc3R1ZGlvLmNvbS9yc3R1ZGlvLWNvbmYtMjAxOS9tZWx0LXRoZS1jbG9jay10aWR5LXRpbWUtc2VyaWVzLWFuYWx5c2lzKSBwdWVkZXMgdmVyIHVuYSBjb25mZXJlbmNpYSBlbiBsYSBxdWUgc2UgZXhwbGljYSBsYXMgcHJpbmNpcGFsZXMgaWRlYXMgZGUgZXN0YSBudWV2YSBmb3JtYSBkZSB0cmFiYWphciBjb24gc2VyaWVzIHRlbXBvcmFsZXMgZW4gZWwgdGlkeXZlcnNlIAoKPGJyPgoKCiMjIDcuIE3DoXMgZGV0YWxsZXMvY29zYXMKCgojIyMgNy4xIGdlb21fc21vb3RoKCkKCllhIGhlbW9zIHVzYWRvIGBnZW9tX3Ntb290aCgpYC4gVW4gYXJndW1lbnRvIGltcG9ydGFudGUgZGUgYGdlb21fc21vb3RoKClgIGVzIGBtZXRob2RgIGNvbiBlbCBxdWUgc2Ugc2VsZWNjaW9uYSBlbCBtw6l0b2RvICBjb24gZWwgcXVlIHNlIG9idGllbmUgbGEgc21vb3RoIGN1cnZlLiBFbCBtw6l0b2RvIHBvciBkZWZlY3RvIGVzIGBtZXRob2QgPSAibG9lc3MiYC4gUHVlZGVzIGNvbnRyb2xhciBlbCBuaXZlbCBkZSBzbW9vdGhpbmcgY29uIGVsIHBhcmFtZXRybyAic3BhbiIsIHF1ZSB2YSBkZSAwICB0byAxIChtYXlvciBzdWF2aXphZG8pLgoKT3RyYXMgb3BjaW9uZXMgcGFyYSBnZW9tX3Ntb290aCgpOgoKLSBtZXRob2QgPSAibG0iIGZpdHMgYSBsaW5lYXIgbW9kZWwsIGdpdmluZyB0aGUgbGluZSBvZiBiZXN0IGZpdC4gCgotIG1ldGhvZCA9ICJybG0iIHdvcmtzIGxpa2UgbG0oKSwgYnV0IHVzZXMgYSByb2J1c3QgZml0dGluZyBhbGdvcml0aG0gc28gdGhhdCBvdXRsaWVycyBkb27igJl0IGFmZmVjdCB0aGUgZml0IGFzIG11Y2guIEl04oCZcyBwYXJ0IG9mIHRoZSBNQVNTIHBhY2thZ2UsIHNvIHJlbWVtYmVyIHRvIGxvYWQgdGhhdCBmaXJzdC4gCgotIHN0YXRfc21vb3RoKHNlID0gRkFMU0UpICwgaW5kaWNhIHNpIHNlIHJlcHJlc2VudGFuIGludGVydmFsb3MgZGUgY29uZmlhbnphIHBhcmEgbGEgbGluZWEgc3Vhdml6YWRhLgoKQWRlbcOhcyBkZSBsb3MgbcOpdG9kb3MgaW1wbGVtZW50YWRvcywgcG9kZW1vcyBlbGVnaXIgbnVlc3RybyBwcm9waW8gbcOpdG9kbywgeWEgc2VhIHVzYW5kbyBlbCBhcmd1bWVudG8gImZvcm11bGEiIG8gZGVmaW5pZW5kbyBudWVzdHJvIHByb3BpbyBtw6l0b2RvIGRlIGFsaXNhZG8gY29tbyBub3MgY3VlbnRhbiBbYXF1w61dKGh0dHBzOi8vZWxpb2NhbXAuZ2l0aHViLmlvL2NvZGlnby1yLzIwMTgvMDYvdHUtcHJvcGlvLWdlb20tc21vb3RoLykgCgoKYGBge3J9CnAgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgCiAgICAgZ2VvbV9wb2ludCgpIApwICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHkgfiBwb2x5KHgsNCkpCmBgYAoKClBvZGVtb3MsIG9idmlhbWVudGUsIGNvbXBhcmFyIGRvcyBtw6l0b2RvcyBkZSBhbGlzYWRvLiBQYXJhIHBvbmVyIG5vbWJyZSBhIGxvcyBkaWZlcmVudGVzIG3DqXRvZG9zIHNlIHB1ZWRlIGhhY2VyIGxvIHNpZ3VpZW50ZTogCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCnAgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgCiAgICAgZ2VvbV9wb2ludCgpCnAgKyBnZW9tX3Ntb290aChhZXMoY29sb3IgPSAibG9lc3MiKSAsIG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gRkFMU0UpICsgCiAgICBnZW9tX3Ntb290aChhZXMoY29sb3IgPSAibG0iKSAgICAsIG1ldGhvZCA9ICJsbSIgICAsIHNlID0gRkFMU0UpCmBgYAoKCjxicj4KCiMjIyA3LjIgw4FyZWFzIGJham8gbGEgY3VydmEKClBhcmEgdW4gcHJvZmVzb3IgZGUgZXN0YWTDrXN0aWNhL2Vjb25vbWV0csOtYSBlcyBpbXBvcnRhbnRlIHNhYmVyIGhhY2VyIGdyw6FmaWNvcyBjb21vIGVzdG9zOgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CnggPC0gYXMuZGF0YS5mcmFtZShjKC0yLCAyKSkKZ2dwbG90KHgsIGFlcyh4KSkgKwogIHN0YXRfZnVuY3Rpb24oZnVuID0gZnVuY3Rpb24oeCkgeyB4KiozIH0sCiAgICAgICAgICAgICAgICBnZW9tID0gImxpbmUiKQoKZ2dwbG90KE5VTEwsIGFlcyh4ID0gYygtMjAsIDIwKSkpICsKICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGZ1bmN0aW9uKHgpIHsgeCoqMyB9LAogICAgICAgICAgICAgICAgZ2VvbSA9ICJsaW5lIikKYGBgCgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KE5VTEwsIGFlcyh4ID0gYygtMywgMykpKSArCiAgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwKICAgICAgICAgICAgICAgIGdlb20gPSAibGluZSIpCmBgYAoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChOVUxMLCBhZXMoeCA9IGMoLTUsIDUpKSkgKwogIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGdlb20gPSAibGluZSIsIHhsaW0gPSBjKC00LCAwKSkgKwogIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGdlb20gPSAiYXJlYSIsIGZpbGwgPSAic3RlZWxibHVlIiwgeGxpbSA9IGMoMCwgNCkpICsKICB4bGltKC01LCA1KQpgYGAKCgoKTG8gYXByZW5kw60gW2FxdcOtXShodHRwczovL2NocmlzdGlhbmJ1cmtoYXJ0LmRlL2Jsb2cvYXJlYV91bmRlcl90aGVfY3VydmUvKS4KCgo8YnI+CgoKIyMjIDcuMyBMYWJlbGxpbmcgbGFzIG9ic2VydmFjaW9uZXMgY29uIGdncmVwZWwKCkVsIGdyw6FmaWNvIGRlIGFiYWpvIHNlIGJhc2EgZW4gW2VzdGUgcG9zdF0oaHR0cHM6Ly9qdWxpYXNpbGdlLmNvbS9ibG9nL2x1YnJpZGF0ZS1sb25kb24tc3RhZ2UvKSBkZSBKdWxpYSBTaWxnZS4gVXRpbGl6YSBlbCBwYXF1ZXRlIFtgZ2dyZXBlbF0oaHR0cHM6Ly9naXRodWIuY29tL3Nsb3drb3cvZ2dyZXBlbCkgcGFyYSBwb2RlciB2ZXIgYSBxdWllbiBwZXJ0ZW5lY2UgbGEgb2JzZXJ2YWNpw7NuIGVuIHVuIGdyw6FmaWNvIGRlIHB1bnRvcy4KCmBgYHtyfQpsaWJyYXJ5KGdncmVwZWwpCmRmIDwtIGdhcG1pbmRlcjo6Z2FwbWluZGVyICU+JSBmaWx0ZXIoeWVhciA9PSAiMjAwNyIpICAlPiUgZmlsdGVyKGNvbnRpbmVudCA9PSAiRXVyb3BlIikKZ2dwbG90KGRmLCBhZXMoZ2RwUGVyY2FwLCBsaWZlRXhwLCBsYWJlbCA9IGNvdW50cnkpKSArIGdlb21fcG9pbnQoKSArCiAgICAgbGFicyh0aXRsZSA9ICJHcsOhZmljbyAxOiBFc3BlcmFuemEgZGUgdmlkYSBmcmVudGUgYSBQSUIgcGVyIGPDoXBpdGEiICwKICAgICAgIGNhcHRpb24gPSAiRGF0b3MgcHJvdmVuaWVudGVzIGRlIGdhcG1pbmRlciIsCiAgICAgICB5ID0gImxpZmVFeHAiLAogICAgICAgeCA9ICJnZHBQZXJjYXAiKSArIGdlb21fc21vb3RoKCkgKwogICAgICAgIGdlb21fbGFiZWxfcmVwZWwoKSAKYGBgCgoKCjxicj4KCiMjIyA3LjQgYEdHYWxseWAgcGFja2FnZQoKSGF5IG11Y2hvcyBwYXF1ZXRlcyBxdWUgcHJvcG9yY2lvbmFuIGZvcm1hcyByw6FwaWRhcyBkZSBoYWNlciBncsOhZmljb3MgY29tbyBwb3IgZWplbXBsbyBlc3RlIGRlbCBwYXF1ZXRlIFtgR0dhbGx5YF0oaHR0cHM6Ly9nZ29iaS5naXRodWIuaW8vZ2dhbGx5L2luZGV4Lmh0bWwpOgoKYGBge3J9CmxpYnJhcnkoR0dhbGx5KQpnZ3BhaXJzKGlyaXMpCmdncGFpcnMoaXJpcyAlPiUgc2VsZWN0KDE6NCkgJT4lIG5hLm9taXQoKSwgcHJvZ3Jlc3MgPSBGQUxTRSwgbG93ZXIgPSBsaXN0KGNvbWJvID0gd3JhcCgiZmFjZXRoaXN0IiwgYmlucz02KSkpCmBgYAoKPGJyPgoKCiMjIyA3LjUgVW4gcG9jbyBtw6FzIGRlIGFub3RhY2lvbmVzIAoKUGFyYSBxdWUgdW4gZ3LDoWZpY28gc2VhIGVmZWN0aXZvIHkgbm9zIGhhZ2EgdmVyIGFsZ8O6biBoZWNobyBvIGNhcmFjdGVyw61zdGljYSBkZSBsb3MgZGF0b3MsIG11Y2hhcyB2ZWNlcyBlcyBwcmVjaXNvIGhhY2VyIGFub3RhY2lvbmVzIGVuIGVsIGdyw6FmaWNvIHBhcmEgc2XDsWFsYXIgbyByZXNhbHRhciBjaWVydG9zIGFzcGVjdG9zIGRlIGVzdGUuIEVuIGVzdGEgc3Vic2VjY2nDs24gcHJlc2VudG8gYWxnw7puIGVqZW1wbG8gZGUgdXNvIGRlIGxhcyBhbm90YWNpb25lcyBlbiBncsOhZmljb3MgZ2dwbG90LgoKCi0gRW4gcHJpbWVyIGx1Z2FyIHVuIGVqZW1wbG8gZGUgSGFkbGV5LiBIYWRsZXkgbm9zIGRpY2UgcXVlIG5vIGhheSBuYWRhIG5vdmVkb3NvIGV4Y2VwdG8gZWwgdXNvIGRlIC1JbmYgYW5kIEluZiBjb21vIHBvc2ljaW9uZXMgcGFyYSByZWZlcmlyc2UgYSBsb3MgbMOtbWl0ZXMgZGVsIGdyw6FmaWNvLCB0aGUgdG9wIGFuZCBib3R0b20gKG9yIGxlZnQgYW5kIHJpZ2h0KSBsaW1pdHMgb2YgdGhlIHBsb3QuCgpgYGB7cn0KcHJlc2lkZW50aWFsIDwtIHN1YnNldChwcmVzaWRlbnRpYWwsIHN0YXJ0ID4gZWNvbm9taWNzJGRhdGVbMV0pCgpwIDwtIGdncGxvdChlY29ub21pY3MpICsgIGdlb21fbGluZShhZXMoZGF0ZSwgdW5lbXBsb3kpKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImJsdWUiLCAicmVkIikpICsKICB4bGFiKCJkYXRlIikgKyAKICB5bGFiKCJ1bmVtcGxveW1lbnQiKQoKIy0gY29taWVuemFuIGxhcyBhbm90YWNpb25lcyAgCnAgKyBnZW9tX3JlY3QoYWVzKHhtaW4gPSBzdGFydCwgeG1heCA9IGVuZCwgZmlsbCA9IHBhcnR5KSwgCiAgICAgICAgICAgICAgeW1pbiA9IC1JbmYsIHltYXggPSBJbmYsIGFscGhhID0gMC4yLCBkYXRhID0gcHJlc2lkZW50aWFsKSArIAogICAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IGFzLm51bWVyaWMoc3RhcnQpKSwgCiAgICAgICAgICAgICAgIGRhdGEgPSBwcmVzaWRlbnRpYWwsIGNvbG91ciA9ICJncmV5NTAiLCBhbHBoYSA9IDAuNSkgKyAKICAgIGdlb21fdGV4dChhZXMoeCA9IHN0YXJ0LCB5ID0gMjUwMCwgbGFiZWwgPSBuYW1lKSwgCiAgICAgICAgICAgICAgIGRhdGEgPSBwcmVzaWRlbnRpYWwsIHNpemUgPSAzLCB2anVzdCA9IDAsIGhqdXN0ID0gMCwgbnVkZ2VfeCA9IDUwKSAKYGBgCgoKVW5hIGZvcm1hIGNvbcO6biBkZSBhbm90YWNpw7NuIGNvbnNpc3RlIGVuIG1hcmNhciBvIHN1YnJheWFyIHVuIGNvbmp1bnRvIGRlIHB1bnRvcy4gUG9yIGVqZW1wbG8gbWFyY2FyIGxvcyBjb2NoZXMgZGUgbGEgbWFyY2EgU3ViYXJ1IGVuIGVsIHNpZ3VpZW50ZSBncsOhZmljby4gU2UgcHVlZGUgaGFjZXIgZGUgbGEgc2lndWllbnRlIG1hbmVyYTogCgoKYGBge3J9CnAgPC0gZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZmlsdGVyKG1wZywgbWFudWZhY3R1cmVyID09ICJzdWJhcnUiKSwgY29sb3VyID0gIm9yYW5nZSIsIHNpemUgPSAzKSArCiAgZ2VvbV9wb2ludCgpIApwCmBgYAoKCkNvbW8gdmVzLCBsbyBxdWUgc2UgaGEgaGVjaG8gZXMgc3VwZXJwb25lciB1bmEgY2FwYSBjb24gc29sbyBsYXMgb2JzZXJ2YWNpb25lcyBkZSBTdWJhcnUgeSBncmFmaWNhcmxhcyBjb24gdW4gcHVudG8gbcOhcyBncmFuZGUgZGUgbG8gaGFiaXR1YWwgeSBjb24gdW4gY29sb3IgbGxhbWF0aXZvLiBFbCBwcm9ibGVtYSBlcyBxdWUgc2UgdmUgcXVlIHNlYW4gbGFzIG9ic2VydmFjaW9uZXMgZGUgU3ViYXJ1LiBFc3RvIHNlIHB1ZWRlIHJlc29sdmVyIGRlIHZhcmlhcyBtYW5lcmFzIHVzYW5kbyBhbm5vdGF0ZSgpOgoKCmBgYHtyfQpwICsgYW5ub3RhdGUoZ2VvbSA9ICJwb2ludCIsIHggPSA1LjUsIHkgPSA0MCwgY29sb3VyID0gIm9yYW5nZSIsIHNpemUgPSAzKSArIAogICAgYW5ub3RhdGUoZ2VvbSA9ICJwb2ludCIsIHggPSA1LjUsIHkgPSA0MCkgKyAKICAgIGFubm90YXRlKGdlb20gPSAidGV4dCIsIHggPSA1LjYsIHkgPSA0MCwgbGFiZWwgPSAic3ViYXJ1IiwgaGp1c3QgPSAibGVmdCIpCmBgYAoKTyBkZSBlc3RhIG90cmEgZm9ybWE6CgpgYGB7cn0KcCArIGFubm90YXRlKGdlb20gPSAiY3VydmUiLCB4ID0gNCwgeSA9IDM1LCB4ZW5kID0gMi42NSwgeWVuZCA9IDI3LCAKICAgICAgICAgICAgICBjdXJ2YXR1cmUgPSAuMywgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDIsICJtbSIpKSkgKwogICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCB4ID0gNC4xLCB5ID0gMzUsIGxhYmVsID0gInN1YmFydSIsIGhqdXN0ID0gImxlZnQiKQpgYGAKCgpFbCBwYXF1ZXRlIFtgZ2dmb3JjZWBdKGh0dHBzOi8vZ2dmb3JjZS5kYXRhLWltYWdpbmlzdC5jb20vKSBlcyB1bmEgZXh0ZW5zacOzbiBhIGdncGxvdDIgcXVlIHB1ZWRlIHNlcnZpciBwYXJhIG11Y2hhcyBjb3NhcywgZW50cmUgZWxsYXMgaGFjZXIgYW5vdGFjaW9uZXMgbyBtYXJjYXMgZW4gZ3LDoWZpY29zIGdncGxvdC4gUG9yIGVqZW1wbG86CgpgYGB7cn0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IFNwZWNpZXMpKSArIAogIGdnZm9yY2U6Omdlb21fbWFya19lbGxpcHNlKGFlcyhsYWJlbCA9IFNwZWNpZXMsIGdyb3VwID0gU3BlY2llcykpCmBgYAoKCkNvbiBgZ2dmb3JjZWAgcG9kZW1vcyBoYXN0YSBzaW11bGFyIHVuIHpvb206CgoKYGBge3J9CmdncGxvdChpcmlzLCBhZXMoUGV0YWwuTGVuZ3RoLCBQZXRhbC5XaWR0aCwgY29sb3VyID0gU3BlY2llcykpICsKICBnZW9tX3BvaW50KCkgKwogIGdnZm9yY2U6OmZhY2V0X3pvb20oeCA9IFNwZWNpZXMgPT0gInZlcnNpY29sb3IiKQpgYGAKCjxicj4KCkVsIHBhcXVldGUvZXh0ZW5zacOzbiBgZ2dmb3JjZWAgZXMgcHJldHR5IGF3ZXNvbWUuIEVuIFtlc3RlIHBvc3RdKGh0dHBzOi8vcnZpZXdzLnJzdHVkaW8uY29tLzIwMTkvMDkvMTkvaW50cm8tdG8tZ2dmb3JjZSkgcHVlZGUgdmVybG8gZW4gYWNjacOzbiBoYWNpZW5kbyBtYXBhcy4KCjxicj4KCk90cm8gZW5mb3F1ZSBwYXJhIGhhY2VyIGFub3RhY2lvbmVzLCBidWVubywgZW4gcmVhbGlkYWQgY2VudHJhc2UgZW4gdW4gZ3J1cG8gZGUgb2JzZXJ2YWNpb25lcyBlcyB1dGlsaXphciBlbCBwYXF1ZXRlIFtgZ2doaWdobGlnaHRgXShodHRwczovL2dpdGh1Yi5jb20veXV0YW5uaWhpbGF0aW9uL2dnaGlnaGxpZ2h0KToKCgpgYGB7cn0KZGYgPC0gZ2FwbWluZGVyOjpnYXBtaW5kZXIgJT4lIGZpbHRlcihjb250aW5lbnQgPT0gIkV1cm9wZSIpCmdncGxvdChkZiwgYWVzKHllYXIsIGxpZmVFeHAsIGdyb3VwID0gY291bnRyeSkpICsgCiAgZ2VvbV9saW5lKCkgKyAKICBnZW9tX3BvaW50KCkgKyAKICBnZ2hpZ2hsaWdodDo6Z2doaWdobGlnaHQoY291bnRyeSAlaW4lIGMoIlNwYWluIiwgIlBvcnR1Z2FsIikpCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBjb2xvciA9IGFzLmZhY3RvcihTcGVjaWVzKSkpICsKICBnZW9tX3BvaW50KCkgKyAKICBnZ2hpZ2hsaWdodDo6Z2doaWdobGlnaHQoKSArIAogIGZhY2V0X3dyYXAodmFycyhTcGVjaWVzKSkKYGBgCgoKCgoKPGJyPgoKCiMjIDguIEFzaXN0ZW50ZXMgcGFyYSBnZ3Bsb3QyCgpBaG9yYSBtaXNtbyBleGlzdGVuIDIgYXNpc3RlbnRlcyBwYXJhIGNyZWFyIGdyw6FmaWNvcyBnZ3Bsb3QgYSB0cmF2w6lzIGRlIGludGVyZmFjZXMgZ3LDoWZpY2FzOiBbYGdnVGhlbWVBc3Npc3RgXShodHRwczovL2dpdGh1Yi5jb20vY2FsbGlncm9zcy9nZ3RoZW1lYXNzaXN0KSB5IFtgZXNxdWlzc2VgXShodHRwczovL2RyZWFtcnMuZ2l0aHViLmlvL2VzcXVpc3NlL2luZGV4Lmh0bWwpIAoKLSBFbCBwYXF1ZXRlIFtgZ2dUaGVtZUFzc2lzdGBdKGh0dHBzOi8vZ2l0aHViLmNvbS9jYWxsaWdyb3NzL2dndGhlbWVhc3Npc3QpIGZhY2lsaXRhIG1lZGlhbnRlIHVuIGFkZGluZyBkZSBSU3R1ZGlvIGxhIGVkaWNpw7NuIGRlIGxvcyBkZXRhbGxlcyBkZSB1biBncsOhZmljbzsgZXMgZGVjaXIsIHB1ZWRlcyBjb21lbnphciBoYWNpZW5kbyB1biBncsOhZmljbyBiw6FzaWNvIGVuIFJTdHVkaW8sIHBhcmEgZGVzcHXDqXMgYWJyaXIgbGEgaW50ZXJmYXogZGUgYGdnVGhlbWVBc3Npc3RgIHBhcmEgbW9kaWZpY2FyIGNvbiBlbCBhc2lzdGVudGUgdmlzdWFsIHRvZG9zIGxvcyBlbGVtZW50b3MgZXN0w6l0aWNvcyBkZSBncsOhZmljbyBjb21vIHTDrXR1bG9zLCBsZXllbmRhcywgY29sb3JlcyBldGMuLi4gZXRjLi4uCgogIFBhcmEgZWxsbywgcHJpbWVybyBoYXMgZGUgaW5zdGFsYXIgZWwgcGFxdWV0ZSBjb24gYGluc3RhbGwucGFja2FnZXMoImdnVGhlbWVBc3Npc3QiKWAsIGRlc3B1w6lzIGNhcmdhcmxvIGNvbiBgbGlicmFyeShnZ1RoZW1lQXNzaXN0KWAuIFVuYSB2ZXogaGFzIGNhcmdhZG8gZWwgcGFxdWV0ZSBlbiBtZW1vcmlhIGhhcyBkZSBzZWxlY2Npb25hciBjb24gZWwgY3Vyc29yIGVsIGPDs2RpZ28gcXVlIGdlbmVyYSBlbCBnZ3Bsb3QgcXVlIHF1aWVyZXMgbW9kaWZpY2FyL3R1bmVhci4gVW5hIHZleiB0aWVuZXMgbWFyY2FkbyBjb24gZWwgcmF0w7NuIGVsIGPDs2RpZ28gZGVsIGdyw6FmaWNvLCBoYXMgZGUgc2VndWlyLCBlbiBSU3R1ZGlvLCBlc3RhIHJ1dGEgZGUgbWVuw7pzOiBgVG9vbHMgPiBBZGRpbmdzID4gQnJvd3NlIEFkZGluZ3MgLi4uYCwgcGFyYSBzZWxlY2Npb25hciBmaW5hbG1lbnRlIGVsIGFkZGluZyBsbGFtYWRvICJnZ3Bsb3QgVGhlbWUgQXNpc3RhbnQiLgoKICBTZSBhYnJpcsOhIHVuIGludGVyZmF6IGRvbmRlIHBvZHLDoXMgbW9kaWZpY2FyIGxhIG1heW9yw61hIGRlIGVsZW1lbnRvcyBkZWwgZ3LDoWZpY28uIEN1YW5kbyBoYXlhcyBkZWphZG8gZWwgZ3LDoWZpY28gYSB0dSBndXN0byBwaW5jaGFzIGVuIGBET05FYCB5IHRlIGRldm9sdmVyw6EgZWwgY8OzZGlnbyBxdWUgcmVwcm9kdWNlIGVsIGdyw6FmaWNvIHRhbCB5IGNvbW8gbG8gZWxlZ2lzdGUgZW4gZWwgaW50ZXJmYXouCgogIFBvZMOpaXMgdmVyIHVuIGVqZW1wbG8gZW4gPGh0dHBzOi8vZ2l0aHViLmNvbS9jYWxsaWdyb3NzL2dndGhlbWVhc3Npc3Q+CgoKPGJyPgoKLSBFbCBwYXF1ZXRlIFtgZXNxdWlzc2VgXShodHRwczovL2RyZWFtcnMuZ2l0aHViLmlvL2VzcXVpc3NlL2luZGV4Lmh0bWwpIHBlcm1pdGUgY3JlYXIgZ3LDoWZpY29zIGdncGxvdCBkZXNkZSBjZXJvIGNvbiB1bmEgaW50ZXJmYXogZ3LDoWZpY2EuCgogIFBhcmEgdXNhcmxvIHRpZW5lcyBxdWUgaW5zdGFsYXJsbyBjb24gYGluc3RhbGwucGFja2FnZXMoImVzcXVpc3NlIilgIHBhcmEgbHVlZ28gYWJyaXJsbyBzaWd1aWVuZG8gZXN0YSBydXRhIGRlIG1lbsO6cyBlbiBSU3R1ZGlvOiBgVG9vbHMgPiBBZGRpbmdzID4gQnJvd3NlIEFkZGluZ3MgLi4uYCBwYXJhIGVsZWdpciBlbCBhZGRpbmcgY29uIG5vbWJyZTogImVzcXVpc3NlIC0gZ2dwbG90IGJ1aWxkZXIiLgoKPGJsb2NrcXVvdGUgY2xhc3M9InR3aXR0ZXItdHdlZXQiPjxwIGxhbmc9ImVuIiBkaXI9Imx0ciI+WW91IG1pZ2h0IG5vdCBiZSBhIHBybyBhdCBnZ3Bsb3QgYnV0IHlvdSBjYW4gc3RpbGwgZG8gYW1hemluZyBzdHVmZiB3aXRoIHRoZSAmIzM5O2VzcXVpc3NlJiMzOTsgcGFja2FnZSB0aGF0IG1ha2VzIGdncGxvdCBxdWl0ZSBlYXN5Ljxicj48YnI+QWZ0ZXIgZG93bmxvYWRpbmcgaXQsIHlvdSB3aWxsIGZpbmQgaXQgdW5kZXIg4oCcQWRkaW5z4oCdIG5hbWVkIGdncGxvdDIgYnVpbGRlciwgY2xpY2sgb24gaXQgdG8gZ2V0IHN0YXJ0ZWQgb24gdGhlIGRyYWcgYW5kIGRyb3AgcGxvdHRpbmfwn6STIDxhIGhyZWY9Imh0dHBzOi8vdC5jby8zMGZCYmd2WWpxIj5waWMudHdpdHRlci5jb20vMzBmQmJndllqcTwvYT48L3A+Jm1kYXNoOyBXZSBhcmUgUi1MYWRpZXMgKEBXZUFyZVJMYWRpZXMpIDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vV2VBcmVSTGFkaWVzL3N0YXR1cy8xMTM5NTkxNzYzNDMyMjIyNzIzP3JlZl9zcmM9dHdzcmMlNUV0ZnciPkp1bmUgMTQsIDIwMTk8L2E+PC9ibG9ja3F1b3RlPiA8c2NyaXB0IGFzeW5jIHNyYz0iaHR0cHM6Ly9wbGF0Zm9ybS50d2l0dGVyLmNvbS93aWRnZXRzLmpzIiBjaGFyc2V0PSJ1dGYtOCI+PC9zY3JpcHQ+IAoKCjxicj4KCiMjIDkuIEdyw6FmaWNvcyBpbnRlcmFjdGl2b3MKCkxvcyBncsOhZmljb3MgaW50ZXJhY3Rpdm9zLCBjb21vIHN1IG5vbWJyZSBpbmRpY2EsIHBlcm1pdGUgYWwgdXN1YXJpbyBpbnRlcmFjdHVhciBjb24gZWwgZ3LDoWZpY28sIGFicmnDqW5kb3NlIHBvc2liaWxpZGFkZXMgY29tbyBjZW50cmFyc2UgZW4gcGFydGUgZGVsIGdyw6FmaWNvICh6b29taW5nKSwgaGlnaGxpZ2h0aW5nLCBvIG1vc3RyYXIgaW5mb3JtYWNpw7NuIGFkaWNpb25hbCBhbCBwaW5jaGFyIGVuIGFsZ8O6biBlbGVtZW50byBkZWwgZ3LDoWZpY28sIGV0YyAuLi4KCkVuIGdlbmVyYWwsIEphdmFTY3JpcHQgKEpTKSBlcyBlbCBsZW5ndWFqZSB1dGlsaXphZG8gcGFyYSBoYWNlciBncsOhZmljb3MgaW50ZXJhY3Rpdm9zIGNvbiBsaWJyZXLDrWFzIGNvbW8gRDMsIENoYXJ0LCBQbG90bHksIFZpcyBIaWdoY2hhcnRzLCAuLi4KClJlY2llbnRlbWVudGUsIGVsIHBhcXVldGUgZGUgUiBbYGh0bWx3aWRnZXRzYF0oaHR0cDovL3d3dy5odG1sd2lkZ2V0cy5vcmcvKSBoYSBmYWNpbGl0YWRvIGVsIHVzbyBkZSBsYXMgbGlicmVyw61hcyBkZSBKUyBlbiBSLiBBY3R1YWxtZW50ZSwgcGFxdWV0ZXMgZGUgUiwgY29tbyBsZWFmbGV0LCBEVCwgZHlncmFwaHMsIG5ldHdvcmtEMyB5IG11Y2hvcyBvdHJvcywgdXRpbGl6YW4gZWwgZnJhbWV3b3JrIHByb3B1ZXN0byBwb3IgaHRtbHdpZGdldHMgcGFyYSBoYWNlciBkaXNwb25pYmxlcyBsb3MgZ3LDoWZpY29zIGludGVyYWN0aXZvcyBkZSBKUyBlbiBSLgoKClBhcmEgZGFyb3MgY3VlbnRhIGRlIGxvIGbDoWNpbCBxdWUgZXMgaGFjZXIgdW4gZ3LDoWZpY28gaW50ZXJhY3Rpdm8gY29uIFIgdXNhcmVtb3MgZWwgcGFxdWV0ZSBbYHBsb3RseWBdKGh0dHBzOi8vcGxvdC5seS9yLykgcXVlIGhhY2UgcG9zaWJsZSB1c2FyIGxhIGxpYnJlcsOtYSBwbG90bHkuanMgZW4gUi4gQ29uIGBwbG90bHlgIHNlIHB1ZWRlbiBoYWNlciBtdWNob3MgdGlwb3MgZGUgZ3LDoWZpY29zLCBwZXJvIHBvciBlamVtcGxvLCBwZXJtaXRlIGNvbiB1bmEgc29sYSBsaW5lYSBjb252ZXJ0aXIgdW4gZ3LDoWZpY28gZ2dwbG90IGVuIGludGVyYWN0aXZvOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShwbG90bHkpCnAgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKyAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKQpnZ3Bsb3RseShwKQpgYGAKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwMSA8LSBwICsgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhTcGVjaWVzKSkgCmdncGxvdGx5KHAxKQoKYGBgCgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcCA8LSBnZ3Bsb3QobXBnLCBhZXMoY2xhc3MpKSArICBnZW9tX2JhcihmaWxsID0gInN0ZWVsYmx1ZSIpICsgY29vcmRfZmxpcCgpCmdncGxvdGx5KHApCmBgYAoKCltBcXXDrV0oaHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS9pbnRlcmFjdGl2ZS1jaGFydHMuaHRtbCkgcHVlZGVzIHZlciB1biBnYWxlcsOtYSBkZSBlamVtcGxvcyBkZSBncsOhZmljb3MgaW50ZXJhY3Rpdm9zIGhlY2hvcyBjb24gUi4gW0FxdcOtXShodHRwczovL21vZGVybmRhdGEucGxvdC5seS9pbnRlcmFjdGl2ZS1yLXZpc3VhbGl6YXRpb25zLXdpdGgtZDMtZ2dwbG90Mi1yc3R1ZGlvLykgdW4gcG9zdCwgeWEgZGUgMjAxNSwgcGFyYSBpbmljaWFyc2UgdW4gcG9jbyBlbiBlc3RvcyB0ZW1hcy4gCgoKSGF5IG11Y2hvcyBwYXF1ZXRlcyBxdWUgcGVybWl0ZW4gaGFjZXIgZ3LDoWZpY29zIGludGVyYWN0aXZvcyBlbiBSLCBwb3IgZWplbXBsbyBbYGxlYWZsZXRgXShodHRwOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC8pLCBxdWUgcGVybWl0ZSBoYWNlciBtYXBhcyBpbnRlcmFjdGl2b3MgbXV5IGbDoWNpbG1lbnRlLgoKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KGxlYWZsZXQpCm0gPC0gbGVhZmxldCgpCm0gPC0gYWRkVGlsZXMobSkKbSA8LSBhZGRNYXJrZXJzKG0sIGxuZyA9IDE3NC43NjgsIGxhdCA9LTM2Ljg1MiwgcG9wdXAgPSAiVGhlIGJpcnRocGxhY2Ugb2YgUiIpCm0KYGBgCgoKVG9kb3MgZXN0b3MgcGFxdWV0ZXNeW0VuIGVsIG1vbWVudG8gZGUgZXNjcmliaXIgZXN0YXMgbm90YXMgZXJhbiAxMDcgcGFxdWV0ZXNdIHF1ZSBwZXJtaXRlbiBoYWNlciBncsOhZmljb3MgaW50ZXJhY3Rpdm9zIGEgdHJhdsOpcyBkZSBgaHRtbHdpZGdldHNgIHNlIHB1ZWRlbiBjb25zdWx0YXIgZW46IDxodHRwOi8vZ2FsbGVyeS5odG1sd2lkZ2V0cy5vcmcvPi4gRG9zIHBhcXVldGVzIHF1ZSBubyBlc3TDoW4gZW4gbGEgZ2FsbGVyeTogW2BBcGV4Y2hhcnRzYF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2FwZXhjaGFydGVyL3ZpZ25ldHRlcy9zdGFydGluZy13aXRoLWFwZXhjaGFydHMuaHRtbCkgeSBbYFRTcGxvdGx5YF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL1RTcGxvdGx5L3ZpZ25ldHRlcy9UU3Bsb3RseS5odG1sKS4KCgpFbiBbSW50ZXJhY3RpdmUgd2ViLWJhc2VkIGRhdGEgdmlzdWFsaXphdGlvbiB3aXRoIFIsIHBsb3RseSwgYW5kIHNoaW55XShodHRwczovL3Bsb3RseS1yLmNvbS8pIGV4cGxpY2FuIG3DoXMgZGV0YWxsYWRhbWVudGUgY29tbyBoYWNlciBncsOhZmljb3MgaW50ZXJhY3Rpdm9zIGVuIFIuIFRlbmRyw6kgcXVlIHJlbGVlciBzdSBzZWNjacOzbiBbU2F2aW5nIGFuZCBlbWJlZGRpbmcgSFRNTF0oaHR0cHM6Ly9wbG90bHktci5jb20vc2F2aW5nLmh0bWwpIHBhcmEgbW9zdHJhciBlbiBlbCB0dXRvcmlhbCBhbGd1bm8gZGUgbG9zIGdyw6FmaWNvcyBkaW7DoW1pY29zIHF1ZSBoZSBoZWNobywgcGVybyBhaG9yYSB0ZW5nbyBwcmlzYSwgbGEgcHLDs3hpbWEgY2xhc2UgZXMgcHJvbnRvIHkgZW1wZXphbW9zIHPDrSBvIHPDrSBnZ3Bsb3QyLgoKSGFkbGV5IHRhbWJpw6luIGVzdMOhIGVzY3JpYmllbmRvIHVuIGJvb2tkb3duIHNvYnJlIFNoaW55OiBbTWFzdGVyaW5nIFNoaW55XShodHRwczovL21hc3RlcmluZy1zaGlueS5vcmcvKS4gRW4gcGFsYWJyYXMgZGUgSGFkbGV5OiBTaGlueSBpcyBhIGZyYW1ld29yayBmb3IgY3JlYXRpbmcgd2ViIGFwcGxpY2F0aW9ucyB1c2luZyBSIGNvZGUuIFBhcmEgdmVyIHF1ZSBzaWduaWZpY2EgZXN0byBwdWVkZXMgdmVyIGxhIHNpZ3VpZW50ZSBbZ2FsZXLDrWEgY29uIGVqZW1wbG9zIGRlIGFwbGljYWNpb25lcyBzaGlueV0oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS9nYWxsZXJ5LykuCgoKIyMjIGdnYW5pbWF0ZSAKCltgZ2dhbmltYXRlYF0oaHR0cHM6Ly9nZ2FuaW1hdGUuY29tLykgZXMgdW4gcGFxdWV0ZSBxdWUgbm8gaGFjZSBncsOhZmljb3MgZGluw6FtaWNvcywgcGVybyBwZXJtaXRlIGFuaW1hciBncsOhZmljb3MgbWVkaWFudGUgbGEgY3JlYWNpw7NuIGNyZWFjacOzbiBkZSBzZWN1ZW5jaWFzIGRlIGdyw6FmaWNvcy4gTWVqb3IgcXVlIGV4cGxpY2FybG8gdmlzaXRhIHN1IHNlY2Npw7NuIGRlIGVqZW1wbG9zIGVuIHN1IHdpa2k6IDxodHRwczovL2dpdGh1Yi5jb20vdGhvbWFzcDg1L2dnYW5pbWF0ZS93aWtpPi4gUG9yIGVqZW1wbG8gW2VzdGUgZWplbXBsb10oaHR0cHM6Ly9naXRodWIuY29tL3Rob21hc3A4NS9nZ2FuaW1hdGUvd2lraS9UcmFja2luZy1vZi1odXJyaWNhbmVzLWFuZC10eXBob29ucykgIG8gIFtlc3RlIGVqZW1wbG9dKGh0dHBzOi8vZ2l0aHViLmNvbS90aG9tYXNwODUvZ2dhbmltYXRlL3dpa2kvV29ybGQtQ3VwLUdvYWwtQW5pbWF0aW9uKSBkZSBqdXJnb2wuIEVuIFtlc3RlIHBvc3RdKGh0dHBzOi8vd3d3LmRhdGFub3ZpYS5jb20vZW4vYmxvZy9nZ2FuaW1hdGUtaG93LXRvLWNyZWF0ZS1wbG90cy13aXRoLWJlYXV0aWZ1bC1hbmltYXRpb24taW4tci8pIGV4cGxpY2FuIGNvbW8gdXNhcmxvLgoKClVuIGVqZW1wbG8gY29uIGxvcyBkYXRvcyBkZSBgZ2FwbWluZGVyYAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2FwbWluZGVyKQpsaWJyYXJ5KGdnYW5pbWF0ZSkKZ2FwbWlkZXJfZXVyb3BlIDwtIGdhcG1pbmRlciAlPiUgZmlsdGVyKGNvbnRpbmVudCA9PSAiRXVyb3BlIikKZ2dwbG90KGdhcG1pZGVyX2V1cm9wZSwgYWVzKGdkcFBlcmNhcCwgbGlmZUV4cCwgc2l6ZSA9IHBvcCwgY29sb3VyID0gY291bnRyeSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC43LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb3VudHJ5X2NvbG9ycykgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDIsIDEyKSkgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgZmFjZXRfd3JhcCh+Y29udGluZW50KSArCiAgIyBIZXJlIGNvbWVzIHRoZSBnZ2FuaW1hdGUgc3BlY2lmaWMgYml0cwogIGxhYnModGl0bGUgPSAnWWVhcjoge2ZyYW1lX3RpbWV9JywgeCA9ICdHRFAgcGVyIGNhcGl0YScsIHkgPSAnbGlmZSBleHBlY3RhbmN5JykgKwogIHRyYW5zaXRpb25fdGltZSh5ZWFyKSArCiAgZWFzZV9hZXMoJ2xpbmVhcicpIApgYGAKCltBcXXDrV0oaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vdGhvbWFzcDg1LzA1MTY5YWQ0NGRkY2M4ZWQ1NmRhNmZmN2JmN2ZiZTM2KSB0aWVuZXMgdW4gZWplbXBsbyBwYXJhIGhhY2VybG8gcGFyYSBsb3MgY2luY28gY29udGluZW50ZXMuIAoKPGJyPgoKCgojIyBCaWJsaW8vZWplbXBsb3MvcmVjdXJzb3MKCgpTb24gcmVjdXJzb3MgcXVlIGhlIHZpc3RvIG8gdXRpbGl6YWRvIG1pZW50cmFzIGVzY3JpYsOtYSBlc3RhIG5vdGFzLCB5IG5vIHF1aWVybyBvbHZpZGFybG9zOgoKCi0gW0PDqWRyaWMgU2NoZXJlcl0oaHR0cHM6Ly9naXRodWIuY29tL1ozdHQvVGlkeVR1ZXNkYXkpOiB1bmEgc2VyaWUgZGUgdmlzdWFsaXphY2lvbmVzIGluY3Jlw61ibGVzIGFzb2NpYWRhcyBhbCBwcm95ZWN0byB0aWR5IFR1ZXNkYXkuCgotIFtkYXRhLXRvLXZpel0oaHR0cHM6Ly93d3cuZGF0YS10by12aXouY29tLyk6IHVuYSBwbGFudGlsbGEgcXVlIHRlIHB1ZWRlIGF5dWRhciBhIGRlY2lkaXIgcXXDqSBncsOhZmljbyB1c2FyLgoKCi0gW3Itc3RhdGlzdGljcy5jb10oIGh0dHA6Ly9yLXN0YXRpc3RpY3MuY28vZ2dwbG90Mi1UdXRvcmlhbC1XaXRoLVIuaHRtbCk6IHR1dG9yaWFsIGBnZ3Bsb3QyYC4KCgotIFtBIGdncGxvdDIgVHV0b3JpYWwgZm9yIEJlYXV0aWZ1bCBQbG90dGluZyBpbiBSXShodHRwczovL2NlZHJpY3NjaGVyZXIubmV0bGlmeS5jb20vMjAxOS8wOC8wNS9hLWdncGxvdDItdHV0b3JpYWwtZm9yLWJlYXV0aWZ1bC1wbG90dGluZy1pbi1yLyk6IHR1dG9yaWFsIGBnZ3Bsb3QyYC4KCi0gW1RoZSBFdm9sdXRpb24gb2YgYSBnZ3Bsb3QgKEVwLiAxKV0oaHR0cHM6Ly9jZWRyaWNzY2hlcmVyLm5ldGxpZnkuY29tLzIwMTkvMDUvMTcvdGhlLWV2b2x1dGlvbi1vZi1hLWdncGxvdC1lcC4tMS8pLiBVbiBwb3N0IG11eSBkaWTDoWN0aWNvIGRvbmRlIHNlIHZhIHRyYW5zZm9ybWFuZG8gdW4gZ3LDoWZpY28gaGFzdGEgaGFjZXJsbyBtdXkgY2h1bG8uCgoKLSBbRURBViBpbmZvXShodHRwczovL2VkYXYuaW5mby9ib3guaHRtbCkuIEJvb2tkb3duIGHDum4gZW4gY29uc3RydWNjacOzbiBjb24gYnVlbm9zIGVqZW1wbG9zLgoKCi0gW1RvbeKAmXMgQ29va2Jvb2sgZm9yIEJldHRlciBWaXpdKGh0dHBzOi8vanRob21hc21vY2suZ2l0aHViLmlvL25mbF9wbG90dGluZ19jb29rYm9vay8jbGljZW5zZSkuIFVuIGJ1ZW4gdHV0b3JpYWwgY29uIGNvbnNlam9zIHkgZGVtw6FzIHNvYnJlIGdyw6FmaWNvcyBnZ3Bsb3QuIFBvciBlamVtcGxvIG1pcmEgbG9zICJVc2VmdWwgY29kZSBjaHVua3MiLgoKCi0gW2dncGxvdDIgUXVpY2sgUmVmZXJlbmNlOiBnZW9tXShodHRwOi8vc2FwZS5pbmYudXNpLmNoL3F1aWNrLXJlZmVyZW5jZS9nZ3Bsb3QyL2dlb20pLiBVbmEgY2hlYXRzaGVldCBwYXJhIHZlciBsb3MgZ2VvbXNfeHgoKSBkaXNwb25pYmxlcwoKLSBbRW1pIFRhbmFrYSAtIDFdKGh0dHBzOi8vZW1pdGFuYWthLm9yZy93b3Jrc2hvcFVUb2t5bzIwMTgvZGF5MS1zZXNzaW9uMDItZGF0YXZpcy5odG1sIzEpLiBTb24gdW5hcyB0cmFucGFyZW5jaWFzIGRlIEVtaSBUYW5ha2EgZG9uZGUgaGFjZSB1biByZXBhc28gZmFudMOhc3RpY28gYSBsYSBnZ3Bsb3QyIGdyYW1tYXIuIEVtaSBUYW5ha2EgZXMgbG8gbWVqb3IgcGFyYSBjb25vY2VyIGxhIHZlcmRhZCBzb2JyZSBnZ3Bsb3QyIGVuIHBvY28gdGllbXBvOyBwb3IgZWplbXBsbyBsYSBzbGlkZSAjNi4KCi0gW0RhdGEgVmlzdWFsaXphdGlvbiBpbiB0aGUgVGlkeXZlcnNlXShodHRwczovL2FsaXNvbi5uZXRsaWZ5LmNvbS91by10aWR5LWJha2VvZmYvIzEpLiBPdHJhcyBmYW50w6FzdGljYXMgdHJhbnNwYXJlbmNpYXMgc29icmUgZ2dwbG90MiwgZXN0YSB2ZXogZGUgQWxpc29uIEhpbGwuCgoKLSBbUHJlZ3VudGFzIHNvYnJlIGdncGxvdDIgZW4gU3RhY2tvdmVyZmxvd10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvdGFnZ2VkL2dncGxvdDIpLiBTZWd1cm9zIHF1ZSBlbmN1ZW50cmFzIHR1IHByZWd1bnRhIHkgYWxndWllbiBsYSBoYSByZXNwb25kaWRvISEKCgotIFtEYXRhIFZpc3VhbGl6YXRpb24gd2l0aCBnZ3Bsb3QyXShodHRwczovL2NlbmdlbC5naXRodWIuaW8vUi1kYXRhLXZpei9kYXRhLXZpc3VhbGl6YXRpb24td2l0aC1nZ3Bsb3QyLmh0bWwpLiBVbiBib29rZG93biBzb2JyZSBnZ3Bsb3QyLiBObyBsbyBoZSBsZcOtZG8gcGVybyB0aWVuZSBidWVuYSBwaW50YSBwYXJhIGluaWNpYXJzZSBjb24gZ2dwbG90LgoKCi0gW3ByYWN0aWNhbGdnXShodHRwczovL3dpbGtlbGFiLm9yZy9wcmFjdGljYWxnZy8pLiBVbiBwYXF1ZXRlIGNvbiBlamVtcGxvcyBkZSA2IHZpc3VhbGl6YWNpb25lcyBleHBsaWNhZGFzIHBhc28gYSBwYXNvLgoKCi0gW3Zpei1wdWJdKGh0dHBzOi8vYnVmZi5seS8ybnpNWlBYKS4gVW4gcmVwbyBkZSBHaXRodWIgY29uIGdyw6FmaWNvcyB5IHZpc3VhbGl6YWNpb25lcywgb2J2aWFtZW50ZSBjb24gZWwgY8OzZGlnby4KCgoKLSBbVGhlIGdncGxvdCBmbGlwYm9va10oaHR0cHM6Ly9ldmFtYWVyZXkuZ2l0aHViLmlvL2dncGxvdF9mbGlwYm9vay9nZ3Bsb3RfZmxpcGJvb2tfeGFyaW5nYW4uaHRtbCMxKS4gT3RyYXMgdHJhbnNwYXJlbmNpYXMgc29icmUgZ2dwbG90MiwgZGUgR2luYSBSZXlub2xkcy4KCgotIFtBcnRpc3RpYyBjb2RpbmcgZm9yIHRoZSB1c2VSXShodHRwczovL3d3dy53aWxsaWFtcmNoYXNlLmNvbS9wb3N0L2FydGlzdGljLWNvZGluZy1mb3ItdGhlLXVzZXItMTItbW9udGhzLW9mLWFydC1qdW5lLykuIFVuIHBvc3QgZGUgV2lsbCBDaGFzZSBwYXJhIGludHJvZHVjaXJzZSBlbiBlbCBtdW5kbyBkZSBoYWNlciBhUnRlIGNvbiBSLiAKCgotIFtZb3UgY2FuIHJlcGxpY2F0ZSBhbG1vc3QgYW55IHBsb3Qgd2l0aCBSXShodHRwczovL3NpbXBseXN0YXRpc3RpY3Mub3JnLzIwMTkvMDgvMjgveW91LWNhbi1yZXBsaWNhdGUtYWxtb3N0LWFueS1wbG90LXdpdGgtZ2dwbG90Mi8pLiBJbXByZXNpb25hbnRlIHBvc3QgZGUgUmFmYWVsIElyaXphcnJ5IGRvbmRlIHJlcGxpY2EgY29uIFIgNSB2aXN1YWxpemFjaW9uZXMuCgotIFtEYXRhLCBtb3ZpZXMgYW5kIGdncGxvdDJdKGh0dHA6Ly9zbWFydGVycG9sYW5kLnBsL2luZGV4LnBocC8yMDE4LzEyL2RhdGEtbW92aWVzLWFuZC1nZ3Bsb3QyLykuIFVub3MgcG9zdGVycyBpbmNyZcOtYmxlcyBoZWNob3MgY29uIFIuCgoKUG9yIMO6bHRpbW8gMyByZXBvcyBkZSBHaXRodWIgYXNvY2lhZG9zIGFsIHByb3llY3RvIFtUaWR5dHVlc2RheV0oaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheSkuIEFic29sdXRhbWVudGUgaW1wcmVzaW9uYW50ZXMhISEKCgotIDxodHRwczovL2dpdGh1Yi5jb20vZ2thcmFtYW5pcy90aWR5dHVlc2RheS90cmVlL21hc3Rlci93ZWVrLTM4PgoKLSA8aHR0cHM6Ly9naXRodWIuY29tL3NwcmVuOWVyL3RpZHl0dWVzZGF5PgoKLSA8aHR0cHM6Ly9naXRodWIuY29tL290aG9tYW50ZWdhenphL2NvZGUtdGlkeXR1ZXNkYXk+CgoKCiMjIE1hcyBiaWJsaW8KCi0gW0EgR3JhbW1hciBvZiBHcmFwaGljcyBmb3IgUHl0aG9uXShodHRwczovL3Bsb3RuaW5lLnJlYWR0aGVkb2NzLmlvL2VuL3N0YWJsZS9pbmRleC5odG1sKS4gYFBsb3RuaW5lYCBpcyBhbiBpbXBsZW1lbnRhdGlvbiBvZiBhIGdyYW1tYXIgb2YgZ3JhcGhpY3MgaW4gUHl0aG9uLCBpdCBpcyBiYXNlZCBvbiBgZ2dwbG90MmAuICAKCi0gW2dncGxvdDJ0b3JdKGh0dHBzOi8vZ2dwbG90MnR1dG9yLmNvbS8pLiBVbiBzaXRpbyB3ZWIgY29uIGJ1ZW5vcyB0dXRvcmlhbGVzIHNvYnJlIGVqZW1wbG9zIGRlIGdyw6FmaWNvcyBnZ3Bsb3QuIFZhbiBtZWpvcmFuZG8gZWwgZ3LDoWZpY28gcG9jbyBhIHBvY28uIFBvciBlamVtcGxvIFtlc3RlIHR1dG9yaWFsIHNvYnJlIHBvd2VybGlmdGluZ10oaHR0cHM6Ly9nZ3Bsb3QydHV0b3IuY29tL3Bvd2VybGlmdGluZy9zcXVhdHMvKQoKLSBVbiBbZWplbXBsbyBkZSBkb3RwbG90XShodHRwczovL2lrYXNobml0c2t5LmdpdGh1Yi5pby8yMDE5L2RvdHBsb3QvKSBJbHlhIEthc2huaXRza3kgZGUgIEFxdcOtIGVzdMOhIGVsIFtnaXN0XShodHRwczovL2dpc3QuZ2l0aHViLmNvbS9pa2FzaG5pdHNreS8yZjNlMmIyYWY2ZjUwOTExYmI3NzViYmNlNmViMGZiOCkuCgoKLSBbUiBHcmFwaGljcyBDb29rYm9va10oaHR0cHM6Ly9yLWdyYXBoaWNzLm9yZy8pCgotLS0tLS0tLS0tLS0tLS0tLS0KCmBgYHtyLCBldmFsID0gRkFMU0UsIGVjaG8gPSBGQUxTRX0KIy0gbnVldmFzIGNvc2FzIGRlIGdncGxvdDIKcCA8LSBnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX2xpbmUoKQphYSA8LSBnZ3Bsb3RfYnVpbGQocCkgIy0gZnVuY2lvbiBudWV2YQoKIy0gZ2FyZmljb3MgdXBzZXQgeSB2ZW5uOiBodHRwczovL3d3dy5saXR0bGVtaXNzZGF0YS5jb20vYmxvZy9zZXQtYW5hbHlzaXMKIy0gaHR0cHM6Ly9kcW4ud2Vic2l0ZS9wb3N0L2ludGVyYWN0aXZlLW1la2tvLWNoYXJ0cy1pbi1yLwoKYGBgCgo=